newrun
69
temp_backup/Server_redundant/create_user.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add current directory to sys.path
|
||||
sys.path.append(os.getcwd())
|
||||
|
||||
from app.db import SessionLocal
|
||||
from app.services.auth_service import auth_service
|
||||
from app.schemas.auth import UserRegister
|
||||
from app.core.security import get_password_hash
|
||||
from app.models.user import User
|
||||
|
||||
def create_user(username, password, email=None):
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Check if user exists
|
||||
existing = db.query(User).filter(User.username == username).first()
|
||||
if existing:
|
||||
print(f"❌ 用户名 '{username}' 已存在")
|
||||
return
|
||||
|
||||
# Prepare registration data
|
||||
# We bypass the code verification by not providing code,
|
||||
# but we set email if provided.
|
||||
# However, auth_service.register sets is_verified=False by default if code is missing.
|
||||
# We might want to manually set is_verified=True after registration for convenience.
|
||||
|
||||
register_data = UserRegister(
|
||||
username=username,
|
||||
password=password,
|
||||
confirm_password=password,
|
||||
email=email,
|
||||
device_id="local_script"
|
||||
)
|
||||
|
||||
# Call register service
|
||||
try:
|
||||
token = auth_service.register(db, register_data)
|
||||
print(f"✅ 用户 '{username}' 注册成功!")
|
||||
|
||||
# Manually verify the user for local convenience
|
||||
user = db.query(User).filter(User.username == username).first()
|
||||
if user:
|
||||
user.is_verified = True
|
||||
user.permissions = "admin" # Grant admin permissions for local test user
|
||||
db.commit()
|
||||
print(f"✅ 已自动验证邮箱并赋予 admin 权限")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 注册失败: {e}")
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.path) < 2:
|
||||
print("Usage: python create_user.py [username] [password]")
|
||||
|
||||
username = "admin"
|
||||
password = "password123"
|
||||
email = "admin@example.com"
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
username = sys.argv[1]
|
||||
if len(sys.argv) > 2:
|
||||
password = sys.argv[2]
|
||||
|
||||
print(f"正在创建用户: {username} ...")
|
||||
create_user(username, password, email)
|
||||
120
temp_backup/Server_redundant/init_db.py
Normal file
@@ -0,0 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
数据库初始化脚本
|
||||
功能:
|
||||
1. 检查数据库连接
|
||||
2. 创建所有定义的表(如果不存在)
|
||||
3. 检查现有表的字段,如果缺失则自动添加
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from sqlalchemy import create_engine, inspect, text
|
||||
from app.core.config import settings
|
||||
from app.db import Base
|
||||
# 导入所有模型以确保它们被注册到 Base.metadata
|
||||
from app.models import user, group, business, session
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def get_engine():
|
||||
"""获取数据库引擎"""
|
||||
# 优先使用环境变量中的配置,如果没有则构建
|
||||
db_url = settings.DATABASE_URL
|
||||
if not db_url:
|
||||
host = os.getenv('DB_HOST', 'localhost')
|
||||
port = os.getenv('DB_PORT', '3306')
|
||||
user = os.getenv('DB_USER', 'root')
|
||||
password = os.getenv('DB_PASSWORD', '')
|
||||
db_name = os.getenv('DB_NAME', 'designer_db')
|
||||
db_url = f"mysql+pymysql://{user}:{password}@{host}:{port}/{db_name}"
|
||||
|
||||
return create_engine(db_url)
|
||||
|
||||
def map_python_type_to_sql(col_type):
|
||||
"""将 SQLAlchemy 类型映射为 MySQL 类型"""
|
||||
type_str = str(col_type).lower()
|
||||
if 'varchar' in type_str:
|
||||
return type_str
|
||||
if 'string' in type_str:
|
||||
length = getattr(col_type, 'length', 255)
|
||||
return f"varchar({length})"
|
||||
if 'integer' in type_str or 'int' in type_str:
|
||||
return "int"
|
||||
if 'boolean' in type_str:
|
||||
return "tinyint(1)"
|
||||
if 'datetime' in type_str:
|
||||
return "datetime"
|
||||
if 'date' in type_str:
|
||||
return "date"
|
||||
if 'float' in type_str:
|
||||
return "float"
|
||||
if 'text' in type_str:
|
||||
return "text"
|
||||
return "varchar(255)" # 默认
|
||||
|
||||
def init_db():
|
||||
logger.info("🔄 开始数据库初始化检查...")
|
||||
|
||||
try:
|
||||
engine = get_engine()
|
||||
inspector = inspect(engine)
|
||||
|
||||
# 1. 创建缺失的表
|
||||
logger.info("📊 检查表结构...")
|
||||
Base.metadata.create_all(bind=engine)
|
||||
logger.info("✅ 基础表结构检查完成")
|
||||
|
||||
# 2. 检查并补充缺失的列
|
||||
logger.info("🔍 检查缺失字段...")
|
||||
|
||||
existing_tables = inspector.get_table_names()
|
||||
|
||||
with engine.connect() as conn:
|
||||
for table_name, table in Base.metadata.tables.items():
|
||||
if table_name not in existing_tables:
|
||||
continue
|
||||
|
||||
# 获取数据库中现有的列
|
||||
existing_columns = [col['name'] for col in inspector.get_columns(table_name)]
|
||||
|
||||
# 检查模型定义的列
|
||||
for column in table.columns:
|
||||
if column.name not in existing_columns:
|
||||
logger.info(f" ➕ 发现缺失字段: {table_name}.{column.name}")
|
||||
|
||||
# 构建 ALTER TABLE 语句
|
||||
col_type = map_python_type_to_sql(column.type)
|
||||
default_val = ""
|
||||
|
||||
# 处理默认值 (简化处理,只处理常见类型)
|
||||
if column.default:
|
||||
arg = column.default.arg
|
||||
if isinstance(arg, (int, float, bool)):
|
||||
if isinstance(arg, bool):
|
||||
arg = 1 if arg else 0
|
||||
default_val = f" DEFAULT {arg}"
|
||||
elif isinstance(arg, str):
|
||||
default_val = f" DEFAULT '{arg}'"
|
||||
|
||||
nullable = "NULL" if column.nullable else "NOT NULL"
|
||||
if column.nullable and not default_val:
|
||||
default_val = " DEFAULT NULL"
|
||||
|
||||
sql = f"ALTER TABLE {table_name} ADD COLUMN {column.name} {col_type} {nullable}{default_val};"
|
||||
logger.info(f" 🚀 执行: {sql}")
|
||||
conn.execute(text(sql))
|
||||
|
||||
conn.commit()
|
||||
logger.info("✅ 数据库同步完成!")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 数据库初始化失败: {e}")
|
||||
# 不抛出异常,以免阻断容器启动(如果是网络波动等临时问题)
|
||||
# 但在生产环境中可能需要抛出
|
||||
|
||||
if __name__ == "__main__":
|
||||
init_db()
|
||||
170
temp_backup/Server_redundant/init_db.sql
Normal file
@@ -0,0 +1,170 @@
|
||||
-- ==========================================
|
||||
-- Database Initialization Script
|
||||
-- Generated at: 2025-12-22 17:54:23.463367
|
||||
-- ==========================================
|
||||
|
||||
-- 1. Select Database and Cleanup Tables
|
||||
USE designer_db;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
DROP TABLE IF EXISTS check_in_config;
|
||||
DROP TABLE IF EXISTS check_in_records;
|
||||
DROP TABLE IF EXISTS features_config;
|
||||
DROP TABLE IF EXISTS plugin_groups;
|
||||
DROP TABLE IF EXISTS points_history;
|
||||
DROP TABLE IF EXISTS vip_config;
|
||||
DROP TABLE IF EXISTS users;
|
||||
DROP TABLE IF EXISTS user_sessions;
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
-- 2. Create Tables
|
||||
CREATE TABLE check_in_config (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||
consecutive_days INTEGER NOT NULL,
|
||||
base_points INTEGER NOT NULL,
|
||||
bonus_points INTEGER NOT NULL,
|
||||
total_points INTEGER NOT NULL,
|
||||
enabled BOOL,
|
||||
created_at DATETIME DEFAULT now(),
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (consecutive_days)
|
||||
);
|
||||
|
||||
CREATE TABLE check_in_records (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
username VARCHAR(50) NOT NULL,
|
||||
check_in_date DATE NOT NULL,
|
||||
points_earned INTEGER NOT NULL,
|
||||
consecutive_days INTEGER NOT NULL,
|
||||
vip_multiplier FLOAT,
|
||||
created_at DATETIME DEFAULT now(),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE features_config (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||
feature_key VARCHAR(50) NOT NULL,
|
||||
feature_name VARCHAR(100) NOT NULL,
|
||||
category VARCHAR(50),
|
||||
points_cost INTEGER,
|
||||
vip_points_cost INTEGER,
|
||||
svip_points_cost INTEGER,
|
||||
enabled BOOL,
|
||||
description TEXT,
|
||||
created_at DATETIME DEFAULT now(),
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE plugin_groups (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
current_version_file VARCHAR(255),
|
||||
comment TEXT,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE points_history (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
username VARCHAR(50) NOT NULL,
|
||||
type VARCHAR(20) NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
balance INTEGER NOT NULL,
|
||||
description VARCHAR(255),
|
||||
created_at DATETIME DEFAULT now(),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE vip_config (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||
vip_type VARCHAR(20) NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
price FLOAT NOT NULL,
|
||||
daily_quota INTEGER NOT NULL,
|
||||
points_multiplier FLOAT,
|
||||
enabled BOOL,
|
||||
description TEXT,
|
||||
created_at DATETIME DEFAULT now(),
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (vip_type)
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
hashed_password VARCHAR(128) NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT now(),
|
||||
group_id INTEGER,
|
||||
permissions TEXT,
|
||||
expire_date DATETIME,
|
||||
email VARCHAR(255),
|
||||
is_verified BOOL,
|
||||
verification_code VARCHAR(6),
|
||||
reset_token VARCHAR(128),
|
||||
reset_token_expire DATETIME,
|
||||
nickname VARCHAR(50),
|
||||
avatar VARCHAR(500),
|
||||
points INTEGER,
|
||||
level INTEGER,
|
||||
vip_type VARCHAR(20),
|
||||
vip_expire DATETIME,
|
||||
vip_daily_quota INTEGER,
|
||||
vip_quota_reset_date DATE,
|
||||
total_check_in_days INTEGER,
|
||||
consecutive_check_in INTEGER,
|
||||
last_check_in_date DATE,
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(group_id) REFERENCES plugin_groups (id)
|
||||
);
|
||||
|
||||
CREATE TABLE user_sessions (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
device_id VARCHAR(128) NOT NULL,
|
||||
active BOOL NOT NULL,
|
||||
expires_at DATETIME,
|
||||
created_at DATETIME NOT NULL DEFAULT now(),
|
||||
login_at DATETIME,
|
||||
logout_at DATETIME,
|
||||
duration_seconds INTEGER,
|
||||
last_seen_at DATETIME,
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(user_id) REFERENCES users (id)
|
||||
);
|
||||
|
||||
-- 3. Insert Initial Data
|
||||
-- Default User Group
|
||||
INSERT INTO plugin_groups (name, comment) VALUES ('default', 'Default User Group');
|
||||
-- VIP Config
|
||||
INSERT INTO vip_config (vip_type, name, price, daily_quota, points_multiplier) VALUES ('vip', 'VIP会员', 30.0, 20, 1.5);
|
||||
INSERT INTO vip_config (vip_type, name, price, daily_quota, points_multiplier) VALUES ('svip', 'SVIP会员', 88.0, -1, 2.0);
|
||||
-- Check-in Config
|
||||
INSERT INTO checkin_config (consecutive_days, base_points, bonus_points, total_points) VALUES (1, 10, 0, 10);
|
||||
INSERT INTO checkin_config (consecutive_days, base_points, bonus_points, total_points) VALUES (3, 10, 5, 15);
|
||||
INSERT INTO checkin_config (consecutive_days, base_points, bonus_points, total_points) VALUES (7, 10, 20, 30);
|
||||
-- Feature Config
|
||||
INSERT INTO feature_configs (feature_key, feature_name, points_cost, category) VALUES ('ai_remove_bg', '智能抠图', 10, 'ai');
|
||||
-- 4. Create Admin User (admin / password123)
|
||||
|
||||
INSERT INTO users (
|
||||
username, hashed_password, email, is_verified, permissions,
|
||||
group_id, nickname, level, vip_type, vip_expire,
|
||||
created_at, points, total_check_in_days, consecutive_check_in, vip_daily_quota
|
||||
) VALUES (
|
||||
'admin',
|
||||
'$2b$12$UsFjs3Jwn5BG7u/RJ1efNuTF4zIsjT.pSm1mQEBXGWKR.3Kakhpmq',
|
||||
'admin@example.com',
|
||||
1,
|
||||
'admin,vip,svip',
|
||||
(SELECT id FROM plugin_groups WHERE name = 'default' LIMIT 1),
|
||||
'Administrator',
|
||||
999,
|
||||
'svip',
|
||||
'2099-12-31 23:59:59',
|
||||
NOW(),
|
||||
0, 0, 0, 0
|
||||
);
|
||||
292
temp_backup/Server_redundant/init_full_db.py
Normal file
@@ -0,0 +1,292 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
完整数据库初始化脚本 (Local Execution)
|
||||
功能:
|
||||
1. 重建数据库(DROP & CREATE DATABASE)
|
||||
2. 创建所有表结构
|
||||
3. 创建默认数据(用户组、VIP配置、签到配置、功能配置)
|
||||
4. 创建默认管理员账户 (admin/password123)
|
||||
|
||||
注意:此脚本设计为在本地或 Docker 容器内运行,直接连接 MySQL。
|
||||
它会读取环境变量或使用默认配置。
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from sqlalchemy.schema import CreateTable
|
||||
|
||||
# 确保可以将当前目录添加到 sys.path
|
||||
sys.path.append(os.getcwd())
|
||||
|
||||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.engine.url import make_url
|
||||
|
||||
# 尝试导入应用模块
|
||||
try:
|
||||
from app.core.config import settings
|
||||
from app.db import Base
|
||||
from app.models import user, group, business, session
|
||||
from app.models.user import User
|
||||
from app.models.group import PluginGroup
|
||||
from app.models.business import FeatureConfig, VipConfig, CheckInConfig
|
||||
from app.core.security import get_password_hash
|
||||
except ImportError:
|
||||
print("❌ 无法导入应用模块,请确保在 Server 目录下运行此脚本")
|
||||
print("示例: python scripts/init_full_db.py")
|
||||
sys.exit(1)
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def generate_sql_file():
|
||||
"""生成完整的初始化 SQL 文件"""
|
||||
logger.info("📝 正在生成 SQL 文件 (init_db.sql) ...")
|
||||
|
||||
# 获取数据库引擎 (用于编译 SQL)
|
||||
# 我们使用一个 mock engine,因为我们只想要 SQL 语句
|
||||
engine = create_engine("mysql+pymysql://", strategy="mock", executor=lambda sql, *args, **kwargs: print(sql.compile(dialect=engine.dialect)))
|
||||
|
||||
# 真正的 engine 用于 dialect 编译
|
||||
compile_engine = create_engine("mysql+pymysql://")
|
||||
|
||||
sql_content = []
|
||||
|
||||
# 1. 准备数据库
|
||||
sql_content.append("-- ==========================================")
|
||||
sql_content.append("-- Database Initialization Script")
|
||||
sql_content.append(f"-- Generated at: {datetime.now()}")
|
||||
sql_content.append("-- ==========================================\n")
|
||||
|
||||
# 既然 DROP DATABASE 被禁用,我们切换到目标数据库,并尝试删除所有表
|
||||
sql_content.append("-- 1. Select Database and Cleanup Tables")
|
||||
sql_content.append("USE designer_db;\n")
|
||||
|
||||
# 获取所有表名并生成 DROP TABLE 语句
|
||||
# 注意:为了处理外键约束,我们先禁用外键检查
|
||||
sql_content.append("SET FOREIGN_KEY_CHECKS = 0;")
|
||||
|
||||
for table in Base.metadata.sorted_tables:
|
||||
sql_content.append(f"DROP TABLE IF EXISTS {table.name};")
|
||||
|
||||
sql_content.append("SET FOREIGN_KEY_CHECKS = 1;\n")
|
||||
|
||||
# 2. 创建表结构
|
||||
sql_content.append("-- 2. Create Tables")
|
||||
for table in Base.metadata.sorted_tables:
|
||||
create_table_sql = CreateTable(table).compile(compile_engine)
|
||||
sql_content.append(str(create_table_sql).strip() + ";\n")
|
||||
|
||||
# 3. 插入初始数据
|
||||
sql_content.append("-- 3. Insert Initial Data")
|
||||
|
||||
# 用户组
|
||||
sql_content.append("-- Default User Group")
|
||||
sql_content.append("INSERT INTO plugin_groups (name, comment) VALUES ('default', 'Default User Group');")
|
||||
|
||||
# VIP 配置
|
||||
sql_content.append("-- VIP Config")
|
||||
sql_content.append("INSERT INTO vip_config (vip_type, name, price, daily_quota, points_multiplier) VALUES ('vip', 'VIP会员', 30.0, 20, 1.5);")
|
||||
sql_content.append("INSERT INTO vip_config (vip_type, name, price, daily_quota, points_multiplier) VALUES ('svip', 'SVIP会员', 88.0, -1, 2.0);")
|
||||
|
||||
# 签到配置
|
||||
sql_content.append("-- Check-in Config")
|
||||
sql_content.append("INSERT INTO checkin_config (consecutive_days, base_points, bonus_points, total_points) VALUES (1, 10, 0, 10);")
|
||||
sql_content.append("INSERT INTO checkin_config (consecutive_days, base_points, bonus_points, total_points) VALUES (3, 10, 5, 15);")
|
||||
sql_content.append("INSERT INTO checkin_config (consecutive_days, base_points, bonus_points, total_points) VALUES (7, 10, 20, 30);")
|
||||
|
||||
# 功能配置
|
||||
sql_content.append("-- Feature Config")
|
||||
sql_content.append("INSERT INTO feature_configs (feature_key, feature_name, points_cost, category) VALUES ('ai_remove_bg', '智能抠图', 10, 'ai');")
|
||||
|
||||
# 4. 创建管理员
|
||||
sql_content.append("-- 4. Create Admin User (admin / password123)")
|
||||
# 计算密码哈希 (这里直接计算一次固定的,避免每次运行不一样)
|
||||
# password123 的 bcrypt hash (示例)
|
||||
# 为了准确,我们还是用 python 算一下
|
||||
admin_hash = get_password_hash("password123")
|
||||
|
||||
# 获取 default group id (假设是 1,因为刚刚插入且是第一个)
|
||||
sql_content.append(f"""
|
||||
INSERT INTO users (
|
||||
username, hashed_password, email, is_verified, permissions,
|
||||
group_id, nickname, level, vip_type, vip_expire,
|
||||
created_at, points, total_check_in_days, consecutive_check_in, vip_daily_quota
|
||||
) VALUES (
|
||||
'admin',
|
||||
'{admin_hash}',
|
||||
'admin@example.com',
|
||||
1,
|
||||
'admin,vip,svip',
|
||||
(SELECT id FROM plugin_groups WHERE name = 'default' LIMIT 1),
|
||||
'Administrator',
|
||||
999,
|
||||
'svip',
|
||||
'2099-12-31 23:59:59',
|
||||
NOW(),
|
||||
0, 0, 0, 0
|
||||
);
|
||||
""")
|
||||
|
||||
# 写入文件
|
||||
output_file = "init_db.sql"
|
||||
with open(output_file, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(sql_content))
|
||||
|
||||
logger.info(f"✅ SQL 文件已生成: {output_file}")
|
||||
print(f"\nSQL 文件已生成: {os.path.abspath(output_file)}")
|
||||
print("您可以直接在 phpMyAdmin 中导入此文件来初始化数据库。")
|
||||
|
||||
|
||||
def get_db_url():
|
||||
"""获取数据库连接 URL"""
|
||||
# 优先使用 settings 中的配置
|
||||
# db_url = settings.DATABASE_URL
|
||||
|
||||
# 用户指定的远程数据库
|
||||
# Host: 103.97.201.136
|
||||
# Port: 3388 (映射到了容器内的 3306)
|
||||
# User: designer_user
|
||||
# Pass: DesignerPass123!
|
||||
db_url = "mysql+pymysql://designer_user:DesignerPass123!@103.97.201.136:3388/designer_db"
|
||||
|
||||
# 如果 settings 是默认的 sqlite,尝试构建 mysql 连接(用于本地开发时的强制覆盖)
|
||||
# if db_url.startswith("sqlite"):
|
||||
# # 默认开发环境 Docker MySQL 配置
|
||||
# db_url = "mysql+pymysql://designer_user:DesignerPass123!@localhost:3306/designer_db"
|
||||
# logger.info(f"⚠️ 检测到 SQLite 配置,切换为默认 MySQL 配置: {db_url}")
|
||||
|
||||
logger.info(f"🔌 使用数据库配置: {db_url}")
|
||||
return db_url
|
||||
|
||||
def recreate_database(engine, db_name):
|
||||
"""重建数据库"""
|
||||
logger.info(f"🗑️ 正在重建数据库: {db_name}...")
|
||||
|
||||
# 获取 root 连接(连接到 mysql 系统库或不指定库)
|
||||
url = make_url(engine.url)
|
||||
root_url = url.set(database='mysql')
|
||||
|
||||
# 使用 AUTOCOMMIT 隔离级别
|
||||
root_engine = create_engine(root_url, isolation_level="AUTOCOMMIT")
|
||||
|
||||
with root_engine.connect() as conn:
|
||||
conn.execute(text(f"DROP DATABASE IF EXISTS {db_name}"))
|
||||
conn.execute(text(f"CREATE DATABASE {db_name} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"))
|
||||
logger.info(f"✅ 数据库 {db_name} 重建完成")
|
||||
|
||||
def seed_initial_data(session):
|
||||
"""填充初始数据"""
|
||||
logger.info("🌱 正在填充初始数据...")
|
||||
|
||||
# 1. 默认用户组
|
||||
if session.query(PluginGroup).filter(PluginGroup.name == "default").count() == 0:
|
||||
logger.info(" - 创建默认用户组 'default'")
|
||||
default_group = PluginGroup(name="default", comment="Default User Group")
|
||||
session.add(default_group)
|
||||
|
||||
# 2. VIP 配置
|
||||
if session.query(VipConfig).count() == 0:
|
||||
logger.info(" - 创建 VIP 配置")
|
||||
session.add(VipConfig(vip_type="vip", name="VIP会员", price=30.0, daily_quota=20, points_multiplier=1.5))
|
||||
session.add(VipConfig(vip_type="svip", name="SVIP会员", price=88.0, daily_quota=-1, points_multiplier=2.0))
|
||||
|
||||
# 3. 签到配置
|
||||
if session.query(CheckInConfig).count() == 0:
|
||||
logger.info(" - 创建签到配置")
|
||||
session.add(CheckInConfig(consecutive_days=1, base_points=10, bonus_points=0, total_points=10))
|
||||
session.add(CheckInConfig(consecutive_days=3, base_points=10, bonus_points=5, total_points=15))
|
||||
session.add(CheckInConfig(consecutive_days=7, base_points=10, bonus_points=20, total_points=30))
|
||||
|
||||
# 4. 功能配置
|
||||
if session.query(FeatureConfig).count() == 0:
|
||||
logger.info(" - 创建功能配置")
|
||||
session.add(FeatureConfig(feature_key="ai_remove_bg", feature_name="智能抠图", points_cost=10, category="ai"))
|
||||
|
||||
session.commit()
|
||||
logger.info("✅ 初始数据填充完成")
|
||||
|
||||
def create_admin_user(session):
|
||||
"""创建管理员用户"""
|
||||
username = "admin"
|
||||
password = "123456"
|
||||
email = "admin@example.com"
|
||||
|
||||
existing = session.query(User).filter(User.username == username).first()
|
||||
if existing:
|
||||
logger.info(f"ℹ️ 管理员用户 '{username}' 已存在,跳过创建")
|
||||
return
|
||||
|
||||
logger.info(f"👤 正在创建管理员用户: {username} ...")
|
||||
|
||||
# 获取默认组
|
||||
default_group = session.query(PluginGroup).filter(PluginGroup.name == "default").first()
|
||||
group_id = default_group.id if default_group else None
|
||||
|
||||
new_user = User(
|
||||
username=username,
|
||||
hashed_password=get_password_hash(password),
|
||||
email=email,
|
||||
is_verified=True,
|
||||
permissions="admin,vip,svip", # 赋予所有权限
|
||||
group_id=group_id,
|
||||
nickname="Administrator",
|
||||
level=999,
|
||||
vip_type="svip",
|
||||
vip_expire=datetime(2099, 12, 31)
|
||||
)
|
||||
|
||||
session.add(new_user)
|
||||
session.commit()
|
||||
logger.info(f"✅ 管理员用户创建成功!(User: {username}, Pass: {password})")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Initialize DesignerCEP Database')
|
||||
parser.add_argument('--generate-sql', action='store_true', help='Generate init_db.sql file only')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.generate_sql:
|
||||
generate_sql_file()
|
||||
return
|
||||
|
||||
logger.info("🚀 开始全量数据库初始化流程")
|
||||
|
||||
try:
|
||||
db_url = get_db_url()
|
||||
engine = create_engine(db_url)
|
||||
|
||||
# 1. 重建数据库
|
||||
# 注意:这会删除现有数据!
|
||||
db_name = make_url(db_url).database
|
||||
recreate_database(engine, db_name)
|
||||
|
||||
# 重新连接到新创建的数据库
|
||||
target_engine = create_engine(db_url)
|
||||
|
||||
# 2. 创建表结构
|
||||
logger.info("🏗️ 正在创建表结构...")
|
||||
Base.metadata.create_all(bind=target_engine)
|
||||
logger.info("✅ 表结构创建完成")
|
||||
|
||||
# 3. 数据填充
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=target_engine)
|
||||
session = SessionLocal()
|
||||
|
||||
try:
|
||||
seed_initial_data(session)
|
||||
create_admin_user(session)
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
logger.info("✨✨✨ 数据库初始化全部完成! ✨✨✨")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 初始化失败: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
7
temp_backup/Server_redundant/tempdemo/client/build.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
|
||||
rem D:\Python37\Scripts\pyinstaller.exe -w -i ./newapp.ico run.py
|
||||
|
||||
pyinstaller -w -i ./newapp.ico run.py
|
||||
|
||||
pause
|
||||
BIN
temp_backup/Server_redundant/tempdemo/client/icons/newapp.ico
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
temp_backup/Server_redundant/tempdemo/client/newapp.ico
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
396
temp_backup/Server_redundant/tempdemo/client/run.py
Normal file
@@ -0,0 +1,396 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import wmi
|
||||
import psutil
|
||||
import threading
|
||||
import qtpy, platform
|
||||
import winreg
|
||||
from qtpy.QtCore import Qt, QMetaObject, Signal, Slot, QEvent
|
||||
from qtpy.QtWidgets import QWidget, QVBoxLayout, QInputDialog, QHBoxLayout, QToolButton, QLabel, QSizePolicy, QSplashScreen
|
||||
from PyQt5.QtGui import QIcon, QPixmap
|
||||
import re
|
||||
import hashlib
|
||||
import configparser
|
||||
from win32com.client import Dispatch
|
||||
import ezdxf
|
||||
import zipfile
|
||||
import PyQt5
|
||||
from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow, QVBoxLayout, QWidget, QTabWidget, QPushButton, QLabel, QVBoxLayout, QWidget, QHBoxLayout, QFrame, QMessageBox
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtCore import Qt
|
||||
import qdarktheme
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
import hashlib
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QGroupBox, \
|
||||
QSpacerItem, QSizePolicy, QMessageBox
|
||||
import pymysql
|
||||
import requests
|
||||
|
||||
BASE_URL = "http://43.134.82.18/psmark"
|
||||
#BASE_URL = "http://127.0.0.1:5001"
|
||||
|
||||
tempdir = ""
|
||||
|
||||
def exception_hook(exctype, value, traceback):
|
||||
# Handle the uncaught exception
|
||||
# 处理未捕获的异常
|
||||
QMessageBox.warning(None, "错误", f"发生了未知的异常:{value}")
|
||||
|
||||
|
||||
class LoginDialog(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("PSMARK登录界面")
|
||||
self.setWindowIcon(QIcon("icons/newapp.ico")) # 设置窗口小图标,替换为您的图标文件路径
|
||||
|
||||
self.resize(300, 200)
|
||||
|
||||
主布局 = QVBoxLayout()
|
||||
|
||||
group1 = QGroupBox("登录验证")
|
||||
group1_layout = QVBoxLayout()
|
||||
|
||||
group2 = QHBoxLayout()
|
||||
label1 = QLabel("用户名")
|
||||
self.edit1 = QLineEdit()
|
||||
self.edit1.setFixedWidth(200)
|
||||
spacer1 = QSpacerItem(40, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
group2.addWidget(label1)
|
||||
group2.addItem(spacer1)
|
||||
group2.addWidget(self.edit1)
|
||||
|
||||
group3 = QHBoxLayout()
|
||||
label2 = QLabel("密码")
|
||||
self.edit2 = QLineEdit()
|
||||
self.edit2.setFixedWidth(200)
|
||||
self.edit2.setEchoMode(QLineEdit.Password) # 设置密码输入框为密文
|
||||
spacer2 = QSpacerItem(40, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
group3.addWidget(label2)
|
||||
group3.addItem(spacer2)
|
||||
group3.addWidget(self.edit2)
|
||||
|
||||
group4 = QHBoxLayout()
|
||||
button1 = QPushButton("登录")
|
||||
button2 = QPushButton("注册")
|
||||
group4.addWidget(button1)
|
||||
group4.addWidget(button2)
|
||||
|
||||
group1_layout.addLayout(group2)
|
||||
group1_layout.addLayout(group3)
|
||||
group1_layout.addLayout(group4)
|
||||
group1.setLayout(group1_layout)
|
||||
|
||||
主布局.addWidget(group1)
|
||||
|
||||
group5 = QHBoxLayout()
|
||||
label3 = QLabel("机器码")
|
||||
self.edit3 = QLineEdit()
|
||||
self.edit3.setFixedWidth(200)
|
||||
self.edit3.setReadOnly(True)
|
||||
self.edit3.setFocusPolicy(Qt.NoFocus)
|
||||
|
||||
# 获取主板序列号并提取数字部分
|
||||
'''try:
|
||||
result = subprocess.run(['wmic', 'baseboard', 'get', 'serialnumber'], stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, text=True)
|
||||
motherboard_serial = result.stdout.strip()
|
||||
|
||||
# 使用正则表达式提取数字
|
||||
motherboard_serial = re.sub(r'\D', '', motherboard_serial)
|
||||
|
||||
# 使用SHA-256加密特征码
|
||||
feature_code = hashlib.sha256(motherboard_serial.encode()).hexdigest()
|
||||
|
||||
# 去掉特征码中的英文字符
|
||||
feature_code = re.sub(r'[a-zA-Z]', '', feature_code)
|
||||
except Exception as e:
|
||||
feature_code = "Error: " + str(e)'''
|
||||
# 计算序列号
|
||||
|
||||
try:
|
||||
feature_code = self.get_computer_code()
|
||||
count = ord(feature_code[0]) + ord(feature_code[1])
|
||||
for _ in range(count):
|
||||
feature_code = hashlib.md5(feature_code.encode()).hexdigest().upper()
|
||||
|
||||
except Exception as e:
|
||||
feature_code = "Error: " + str(e)
|
||||
|
||||
self.edit3.setText(feature_code) # 将加密后的特征码设置为 "特征码" 输入框的文本
|
||||
self.rem_user()
|
||||
|
||||
spacer3 = QSpacerItem(10, 10, QSizePolicy.Fixed, QSizePolicy.Minimum)
|
||||
group5.addWidget(label3)
|
||||
group5.addItem(spacer3)
|
||||
group5.addWidget(self.edit3)
|
||||
|
||||
主布局.addLayout(group5)
|
||||
|
||||
self.setLayout(主布局)
|
||||
|
||||
# 链接登录的点击事件
|
||||
button1.clicked.connect(self.slot_login)
|
||||
# 连接注册按钮的点击事件
|
||||
button2.clicked.connect(self.register)
|
||||
|
||||
def get_computer_code(self):
|
||||
computer_code = ''
|
||||
c = wmi.WMI()
|
||||
for cpu in c.Win32_Processor():
|
||||
computer_code += cpu.ProcessorId.strip()
|
||||
|
||||
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\SQMClient", 0, winreg.KEY_READ | winreg.KEY_WOW64_64KEY)
|
||||
# 读取设备ID
|
||||
computer_code += winreg.QueryValueEx(key, "MachineId")[0].strip()
|
||||
# 关闭注册表
|
||||
winreg.CloseKey(key)
|
||||
|
||||
computer_code += str(subprocess.check_output('wmic csproduct get uuid').split(b'\n')[1].strip())
|
||||
|
||||
return computer_code
|
||||
|
||||
def register(self):
|
||||
|
||||
dialog = RegisterDialog()
|
||||
if dialog.exec() != QDialog.Accepted:
|
||||
return
|
||||
|
||||
r = requests.post(BASE_URL + f"/register", data={
|
||||
"username":dialog.get用户名(),
|
||||
"password":dialog.get密码(),
|
||||
"code":self.edit3.text(),
|
||||
"adminpassword":"qwe123456",
|
||||
"truename":dialog.get姓名(),
|
||||
"phone":dialog.get手机号(),
|
||||
"company":dialog.get公司名(),
|
||||
"address":dialog.get地址(),
|
||||
})
|
||||
|
||||
if r.text == "success":
|
||||
QMessageBox.information(self, "成功", "注册成功!")
|
||||
|
||||
elif r.text == 'exist':
|
||||
QMessageBox.critical(self, "错误", "用户已存在!")
|
||||
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", f"注册失败!{r.text}")
|
||||
|
||||
#记住密码
|
||||
def rem_user(self):
|
||||
global tempdir
|
||||
code = self.edit3.text()
|
||||
|
||||
r = requests.post(BASE_URL + f"/query?code={code}")
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
dname = d
|
||||
tempdir = dname + str(time.time())
|
||||
os.makedirs(tempdir)
|
||||
|
||||
filepath = os.path.join(tempdir, "result.zip")
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(r.content)
|
||||
|
||||
with zipfile.ZipFile(filepath, 'r') as zip_ref:
|
||||
zip_ref.extractall(tempdir)
|
||||
|
||||
with open(os.path.join(tempdir, "userinfo.txt"), 'r', encoding="utf-8") as f:
|
||||
userinfo = f.read()
|
||||
|
||||
if userinfo == 'error':
|
||||
return
|
||||
|
||||
self.edit1.setText(userinfo.split("\n")[0])
|
||||
self.edit2.setText(userinfo.split("\n")[1])
|
||||
|
||||
def slot_login(self):
|
||||
global tempdir
|
||||
user_name = self.edit1.text()
|
||||
user_password = self.edit2.text()
|
||||
code = self.edit3.text()
|
||||
|
||||
# 判断缓存是否存在
|
||||
with open(os.path.join(tempdir, "userinfo.txt"), 'r', encoding="utf-8") as f:
|
||||
userinfo = f.read()
|
||||
if not (userinfo.split("\n")[0] == user_name and userinfo.split("\n")[1] == user_password):
|
||||
r = requests.post(BASE_URL + f"/query?code={code}&username={user_name}&password={user_password}")
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
dname = d
|
||||
tempdir = dname + str(time.time())
|
||||
os.makedirs(tempdir)
|
||||
|
||||
filepath = os.path.join(tempdir, "result.zip")
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(r.content)
|
||||
|
||||
with zipfile.ZipFile(filepath, 'r') as zip_ref:
|
||||
zip_ref.extractall(tempdir)
|
||||
|
||||
with open(os.path.join(tempdir, "userinfo.txt"), 'r', encoding="utf-8") as f:
|
||||
userinfo = f.read()
|
||||
|
||||
if userinfo == 'error':
|
||||
QMessageBox.critical(self, "错误", "机器码或账号密码错误!")
|
||||
return
|
||||
|
||||
with zipfile.ZipFile(os.path.join(tempdir, "data.zip"), 'r') as zip_ref:
|
||||
zip_ref.extractall(tempdir)
|
||||
|
||||
#print(tempdir)
|
||||
|
||||
cwd = os.getcwd()
|
||||
sys.path.insert(0, tempdir)
|
||||
os.chdir(tempdir)
|
||||
|
||||
import piece_decorative
|
||||
piece_decorative.config = configparser.ConfigParser()
|
||||
|
||||
os.chdir(cwd)
|
||||
piece_decorative.config.read('config.ini', encoding='utf-8')
|
||||
piece_decorative.PSname = piece_decorative.config.get('程序配置', 'PSname')
|
||||
os.chdir(tempdir)
|
||||
|
||||
import newMark
|
||||
#self.hide()
|
||||
self.window = newMark.MainWindow()
|
||||
self.window.show()
|
||||
self.close()
|
||||
#print("run")
|
||||
#newMark.run()
|
||||
|
||||
|
||||
def show_warning_message(self):
|
||||
# 弹出警告消息框
|
||||
QMessageBox.critical(self, "错误", "请联系管理员 17520145271!")
|
||||
# warning_message = QMessageBox()
|
||||
# warning_message.setIcon(QMessageBox.Warning)
|
||||
# warning_message.setWindowTitle("警告")
|
||||
# warning_message.setText("请联系管理员 17520145271")
|
||||
# warning_message.exec_()
|
||||
|
||||
from PyQt5.QtWidgets import QDialogButtonBox, QFormLayout
|
||||
from PyQt5.QtGui import QIntValidator
|
||||
import re
|
||||
class RegisterDialog(QDialog):
|
||||
'''注册对话框'''
|
||||
def __init__(self):
|
||||
super(RegisterDialog,self).__init__()
|
||||
self.init_gui()
|
||||
|
||||
def init_gui(self):
|
||||
#设置dialog窗口标题
|
||||
self.setWindowTitle("注册")
|
||||
# 设置界面尺寸大小
|
||||
self.resize(500, 260)
|
||||
|
||||
self.用户名QLineEdit = QLineEdit()
|
||||
self.密码QLineEdit = QLineEdit()
|
||||
self.密码QLineEdit.setEchoMode(QLineEdit.Password)
|
||||
self.确认密码QLineEdit = QLineEdit()
|
||||
self.确认密码QLineEdit.setEchoMode(QLineEdit.Password)
|
||||
self.姓名QLineEdit = QLineEdit()
|
||||
self.手机号QLineEdit = QLineEdit()
|
||||
#self.手机号QLineEdit.setValidator(QIntValidator())
|
||||
self.公司名QLineEdit = QLineEdit()
|
||||
self.地址QLineEdit = QLineEdit()
|
||||
|
||||
self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
|
||||
self.buttons.accepted.connect(self.check)
|
||||
self.buttons.rejected.connect(self.reject)
|
||||
|
||||
# 表单布局,当然也可以使用其它布局方式
|
||||
layout = QFormLayout(self)
|
||||
layout.addRow('用户名(用于登录):', self.用户名QLineEdit)
|
||||
layout.addRow('密码(用于登录):', self.密码QLineEdit)
|
||||
layout.addRow('确认密码:', self.确认密码QLineEdit)
|
||||
layout.addRow('姓名:', self.姓名QLineEdit)
|
||||
layout.addRow('手机号(+86):', self.手机号QLineEdit)
|
||||
layout.addRow('公司名:', self.公司名QLineEdit)
|
||||
layout.addRow('地址:', self.地址QLineEdit)
|
||||
layout.addRow(self.buttons)
|
||||
|
||||
def get用户名(self):
|
||||
return self.用户名QLineEdit.text().strip()
|
||||
|
||||
def get密码(self):
|
||||
return self.密码QLineEdit.text().strip()
|
||||
|
||||
def get确认密码(self):
|
||||
return self.确认密码QLineEdit.text().strip()
|
||||
|
||||
def get姓名(self):
|
||||
return self.姓名QLineEdit.text().strip()
|
||||
|
||||
def get手机号(self):
|
||||
return self.手机号QLineEdit.text().strip()
|
||||
|
||||
def get公司名(self):
|
||||
return self.公司名QLineEdit.text().strip()
|
||||
|
||||
def get地址(self):
|
||||
return self.地址QLineEdit.text().strip()
|
||||
|
||||
def check(self):
|
||||
if self.get用户名() == '' or self.get密码() == '' or self.get姓名() == '' or self.get手机号() == '' or self.get公司名() == '' or self.get地址() == '':
|
||||
QMessageBox.critical(self, "错误", "信息不完整!")
|
||||
return
|
||||
|
||||
if self.get密码() != self.get确认密码():
|
||||
QMessageBox.critical(self, "错误", "两次密码不一致!")
|
||||
return
|
||||
|
||||
t = re.compile(r'[1-9][0-9]{10}')
|
||||
s = re.search(t, self.get手机号())
|
||||
if (not s) or (not self.get手机号().startswith('1')) or (len(self.get手机号()) != 11):
|
||||
QMessageBox.critical(self, "错误", "手机号格式错误!")
|
||||
return
|
||||
|
||||
self.accept()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
app3 = QApplication(sys.argv)
|
||||
sys.excepthook = exception_hook # 设置全局异常处理
|
||||
|
||||
splash = QSplashScreen()
|
||||
splash.setPixmap(QPixmap('./splash.png'))
|
||||
splash.show()
|
||||
app3.processEvents()
|
||||
|
||||
qdarktheme.setup_theme(
|
||||
custom_colors={
|
||||
"[dark]": {
|
||||
"background": "#4d4d4d",
|
||||
"foreground": "#ffffff",
|
||||
"primary": "#ffffff",
|
||||
"border": "#717070",
|
||||
}
|
||||
}
|
||||
)
|
||||
login_dialog = LoginDialog()
|
||||
login_dialog.show()
|
||||
splash.finish(login_dialog) # 启动画面完成启动
|
||||
# window = MainWindow()
|
||||
# window.show()
|
||||
r = app3.exec_()
|
||||
time.sleep(1)
|
||||
os.chdir("C:")
|
||||
try:
|
||||
if tempdir != "":
|
||||
shutil.rmtree(tempdir, ignore_errors=True)
|
||||
#print("temp dir remove ok:", tempdir)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
sys.exit(r)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
51
temp_backup/Server_redundant/tempdemo/client/run.spec
Normal file
@@ -0,0 +1,51 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['run.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='run',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['newapp.ico'],
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='run',
|
||||
)
|
||||
BIN
temp_backup/Server_redundant/tempdemo/client/splash.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
4
temp_backup/Server_redundant/tempdemo/client/程序配置.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
[程序配置]
|
||||
PSname = Photoshop.Application.120
|
||||
2740
temp_backup/Server_redundant/tempdemo/psmark/JSX1.py
Normal file
1094
temp_backup/Server_redundant/tempdemo/psmark/JSX10.py
Normal file
1066
temp_backup/Server_redundant/tempdemo/psmark/JSX11.py
Normal file
191
temp_backup/Server_redundant/tempdemo/psmark/JSX12.py
Normal file
@@ -0,0 +1,191 @@
|
||||
dxf12_jscode = """
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
批量套数写入()
|
||||
|
||||
function 批量套数写入() {
|
||||
app.preferences.rulerUnits = Units.CM;
|
||||
var 主文档 = app.activeDocument;
|
||||
var 主文档名称 = 主文档.name;
|
||||
|
||||
// 遍历当前打开的文档
|
||||
for (var i = 0; i < app.documents.length; i++) {
|
||||
var document = app.documents[i];
|
||||
var documentName = document.name;
|
||||
|
||||
// 判断文档名称是否与主文档名称不相同
|
||||
if (documentName !== 主文档名称) {
|
||||
// 设置当前文档为活动文档
|
||||
app.activeDocument = document;
|
||||
|
||||
var 匹配图层数组 = 遍历图层查找P1();
|
||||
|
||||
// 遍历匹配图层数组
|
||||
for (var j = 0; j < 匹配图层数组.length; j++) {
|
||||
var 当前匹配图层 = 匹配图层数组[j];
|
||||
}
|
||||
// 选中当前匹配图层
|
||||
|
||||
|
||||
// 获取当前选区
|
||||
var currentSelection = app.activeDocument.selection;
|
||||
|
||||
// 确保当前选区不为空且为矩形选区
|
||||
if (currentSelection != null && currentSelection.hasOwnProperty('bounds')) {
|
||||
// 进行缩放操作
|
||||
|
||||
app.activeDocument.activeLayer = 当前匹配图层;
|
||||
|
||||
// 载入选区
|
||||
载入选区();
|
||||
// 获取当前选区的坐标
|
||||
var bounds = currentSelection.bounds;
|
||||
|
||||
// 获取当前选区的宽度和高度(以 cm 为单位)
|
||||
var resolution = app.activeDocument.resolution;
|
||||
var widthInCM = (bounds[2].as("cm") - bounds[0].as("cm"));
|
||||
var heightInCM = (bounds[3].as("cm") - bounds[1].as("cm"));
|
||||
var 当前P1图层宽高信息 = "宽度:" + widthInCM.toFixed(2) + "cm,高度:" + heightInCM.toFixed(2) + "cm";
|
||||
|
||||
// 获取当前裁片套数
|
||||
var 搜索关键词 = "P1";
|
||||
var 匹配的图层数量 = 获取匹配图层数量(搜索关键词);
|
||||
var 当前裁片套数 = "一段" + 匹配的图层数量 + "件";
|
||||
|
||||
// 创建新图层并设置名称
|
||||
/// var 新图层 = 主文档.artLayers.add();
|
||||
// var 新图层 = "当前文档宽度缩水" + 宽度值 + "高度缩水" + 高度值;
|
||||
|
||||
// 移动选区到新图层
|
||||
// currentSelection.cut();
|
||||
// app.activeDocument.paste();
|
||||
// alert(当前裁片套数);
|
||||
// alert( 当前P1图层宽高信息);
|
||||
// 文件简介写入
|
||||
文件简介写入(当前裁片套数, 当前P1图层宽高信息 );
|
||||
|
||||
// 这里删除了文件简介的写入将缩水值修改按钮
|
||||
// 调整图像尺寸(宽度值, 高度值);
|
||||
////这里取消了保存功能 为了防止运行的时候变卡
|
||||
//activeDocument.save();
|
||||
} else {
|
||||
// alert("没有找到匹配的图层。");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
app.activeDocument=主文档
|
||||
// alert("写入信息成功","来自左威的提醒");
|
||||
}
|
||||
|
||||
function 文件简介写入(当前裁片套数, 当前P1图层宽高信息) {
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("fileInfo"));
|
||||
r.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putString(stringIDToTypeID("caption"), 当前裁片套数);
|
||||
d1.putString(stringIDToTypeID("keywords"), 当前P1图层宽高信息);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("fileInfo"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 调整图像尺寸(宽度值, 高度值) {
|
||||
var 动作描述 = new ActionDescriptor();
|
||||
动作描述.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("percentUnit"), 100 + 宽度值);
|
||||
动作描述.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("percentUnit"), 100 + 高度值);
|
||||
动作描述.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("imageSize"), 动作描述, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 遍历图层查找P1() {
|
||||
var 匹配图层数组 = [];
|
||||
var 文档 = app.activeDocument;
|
||||
|
||||
// 遍历所有图层
|
||||
function 遍历所有图层(图层) {
|
||||
if (图层.typename === "LayerSet") {
|
||||
for (var i = 0; i < 图层.layers.length; i++) {
|
||||
遍历所有图层(图层.layers[i]);
|
||||
}
|
||||
} else {
|
||||
var 图层名分割数组 = 图层.name.split("-"); // 假设分割符是 "_"
|
||||
if (图层名分割数组[0] === "P1") { // 精确匹配
|
||||
匹配图层数组.push(图层);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 开始遍历
|
||||
for (var j = 0; j < 文档.layers.length; j++) {
|
||||
遍历所有图层(文档.layers[j]);
|
||||
}
|
||||
|
||||
return 匹配图层数组;
|
||||
}
|
||||
|
||||
|
||||
function 获取匹配图层数量(搜索关键词) {
|
||||
var 匹配图层数量 = 0;
|
||||
|
||||
// 递归遍历图层及其子图层
|
||||
function 遍历图层(图层) {
|
||||
if (图层.typename === "LayerSet") {
|
||||
for (var i = 0; i < 图层.layers.length; i++) {
|
||||
遍历图层(图层.layers[i]);
|
||||
}
|
||||
} else {
|
||||
// 进行模糊匹配和精确分割匹配
|
||||
if (图层.name.indexOf(搜索关键词) !== -1 && 精确分割匹配图层(图层.name, 搜索关键词)) {
|
||||
匹配图层数量++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 精确分割匹配图层名
|
||||
function 精确分割匹配图层(图层名, 搜索词) {
|
||||
var 图层名分割数组 = 图层名.split("-"); // 假设分割符是 "_"
|
||||
return 图层名分割数组[0] === 搜索词;
|
||||
}
|
||||
|
||||
var 当前文档 = app.activeDocument;
|
||||
var 所有图层 = 当前文档.layers;
|
||||
|
||||
for (var i = 0; i < 所有图层.length; i++) {
|
||||
遍历图层(所有图层[i]);
|
||||
}
|
||||
|
||||
return 匹配图层数量;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
982
temp_backup/Server_redundant/tempdemo/psmark/JSX13.py
Normal file
@@ -0,0 +1,982 @@
|
||||
dxf13_jscode = """
|
||||
|
||||
|
||||
function 裁片射出宽高缩放() {
|
||||
app.preferences.rulerUnits = Units.PIXELS
|
||||
var 主文档 = app.activeDocument;
|
||||
var 主文档名称 = 主文档.name;
|
||||
|
||||
// 遍历当前打开的文档
|
||||
for (var i = 0; i < app.documents.length; i++) {
|
||||
var document = app.documents[i];
|
||||
var documentName = document.name;
|
||||
|
||||
// 判断文档名称是否与主文档名称不相同
|
||||
if (documentName !== 主文档名称) {
|
||||
app.activeDocument = document;
|
||||
遍历图层();
|
||||
}
|
||||
}
|
||||
|
||||
function 遍历图层() {
|
||||
var layerNames = []; // 用于存储图层名称的数组
|
||||
var currentDocument = app.activeDocument;
|
||||
|
||||
for (var j = 0; j < currentDocument.layers.length; j++) {
|
||||
var layer = currentDocument.layers[j];
|
||||
var layerName = layer.name;
|
||||
layerNames.push(layerName);
|
||||
}
|
||||
|
||||
// 逐个处理图层
|
||||
for (var k = 0; k < layerNames.length; k++) {
|
||||
var 当前图层名称 = layerNames[k];
|
||||
// $.writeln("图层名称:" + 当前图层名称);
|
||||
// alert(当前图层名称);
|
||||
|
||||
var parts = 当前图层名称.split("-");
|
||||
if (parts.length > 0) {
|
||||
var 裁片名称 = parts[0];
|
||||
app.activeDocument = 主文档;
|
||||
$.writeln(裁片名称);
|
||||
初始化模板裁片名称 = 当前图层名称.split("-");
|
||||
初始化码数裁片名称 = 当前图层名称.split("_");
|
||||
大货组名称 =初始化模板裁片名称[0]+("-大货裁片")
|
||||
实际裁片名称 = 初始化模板裁片名称[0]+"-"+初始化码数裁片名称[2]
|
||||
$.writeln(大货组名称);
|
||||
$.writeln(实际裁片名称);
|
||||
var 空白裁片模板 = app.activeDocument.layerSets.getByName(大货组名称).layers.getByName(实际裁片名称 );
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
载入选区()
|
||||
|
||||
var 边距 = 获取当前选区四边距();
|
||||
var 当前选区高度=边距.bottom-边距.top
|
||||
var 当前选区宽度=边距.right-边距.left
|
||||
var 高度转毫米 = pixelsToMillimeters(当前选区高度);
|
||||
var 宽度转毫米 = pixelsToMillimeters(当前选区宽度);
|
||||
|
||||
var 搜索词 = 裁片名称;
|
||||
var 匹配图层数组 = 匹配图层名(搜索词);
|
||||
|
||||
// 显示匹配的图层列表
|
||||
if (匹配图层数组.length > 0) {
|
||||
var 图层列表文本 = "匹配的图层列表:";
|
||||
for (var i = 0; i < 匹配图层数组.length; i++) {
|
||||
if (i !== 0) {
|
||||
图层列表文本 += " ";
|
||||
}
|
||||
图层列表文本 += 匹配图层数组[i].name;
|
||||
}
|
||||
var 数据解析分割=图层列表文本.split("_");
|
||||
//var 实际套花名称=名称部分[0]
|
||||
var 基码图层宽度 = parseFloat(数据解析分割[1]);
|
||||
var 基码图层高度 = parseFloat(数据解析分割[2]);
|
||||
var 缩放比例高度=高度转毫米/基码图层高度*100
|
||||
var 缩放比例宽度=宽度转毫米/基码图层宽度*100
|
||||
// alert(基码图层宽度);
|
||||
} else {
|
||||
alert("没有找到匹配的图层。");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
$.writeln("上边距:" + 边距.top);
|
||||
$.writeln("左边距:" + 边距.left);
|
||||
$.writeln("下边距:" + 边距.bottom);
|
||||
$.writeln("右边距:" + 边距.right);
|
||||
*/8
|
||||
// 示例用法:
|
||||
var 毫米 = 300;
|
||||
var 每英寸像素数 = app.activeDocument.resolution; // 获取当前文档的分辨率(每英寸像素数)
|
||||
var 扩展像素 = 毫米转像素(毫米, 每英寸像素数);
|
||||
|
||||
var 裁切上边距= 边距.top-扩展像素
|
||||
var 裁切左边距= 边距.left-扩展像素
|
||||
var 裁切下边距= 边距.bottom+扩展像素
|
||||
var 裁切右边距= 边距.right+扩展像素
|
||||
$.writeln(裁切上边距);
|
||||
$.writeln(裁切左边距);
|
||||
$.writeln(裁切下边距);
|
||||
$.writeln(裁切右边距);
|
||||
裁切图层(裁切上边距,裁切左边距,裁切下边距,裁切右边距)
|
||||
|
||||
var 空白裁片模板 = app.activeDocument.layerSets.getByName(大货组名称).layers.getByName(实际裁片名称);
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
载入选区()
|
||||
var 缩放定位点的中心坐标=获取当前缩放定位点选区四边距()
|
||||
var 缩放定位点的Y轴坐标=缩放定位点的中心坐标.top2+(缩放定位点的中心坐标.bottom2-缩放定位点的中心坐标.top2)/2
|
||||
var 缩放定位点的X轴坐标=缩放定位点的中心坐标.left2+(缩放定位点的中心坐标.right2-缩放定位点的中心坐标.left2)/2
|
||||
$.writeln("Y轴中心坐标"+缩放定位点的Y轴坐标);
|
||||
$.writeln("X轴中心坐标"+缩放定位点的X轴坐标);
|
||||
|
||||
var 裁片 = app.activeDocument.layers.getByName(裁片名称);
|
||||
app.activeDocument.activeLayer = 裁片
|
||||
//var 空白裁片模板 = app.activeDocument.layerSets.getByName(大货组名称).layers.getByName(实际裁片名称 );
|
||||
//app.activeDocument.activeLayer = 空白裁片模板;
|
||||
取消选择()
|
||||
|
||||
图层按照缩放定位点进行宽高缩放(缩放定位点的X轴坐标,缩放定位点的Y轴坐标,缩放比例高度,缩放比例宽度)
|
||||
|
||||
// var 裁片 = app.activeDocument.layers.getByName(裁片名称);
|
||||
// app.activeDocument.activeLayer = 裁片;
|
||||
|
||||
var 空白裁片模板 = app.activeDocument.layerSets.getByName(大货组名称).layers.getByName(实际裁片名称 );
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
载入选区()
|
||||
var 裁片 = app.activeDocument.layers.getByName(裁片名称);
|
||||
app.activeDocument.activeLayer = 裁片
|
||||
添加图层蒙版()
|
||||
应用图层蒙版()
|
||||
裁片.copy();
|
||||
历史记录回退()
|
||||
app.activeDocument = currentDocument;
|
||||
图层选择(当前图层名称);
|
||||
载入选区();
|
||||
粘贴图层();
|
||||
|
||||
取消选择();
|
||||
// app.refresh();
|
||||
|
||||
|
||||
var 裁片名称 = 当前图层名称.split("_");
|
||||
if (裁片名称.length > 1) {
|
||||
var 角度信息 = 裁片名称[1];
|
||||
|
||||
if (角度信息 === "180" || 角度信息 === "-180") {
|
||||
自由变换();
|
||||
} else if (角度信息 === "-90") {
|
||||
逆时针90旋转()
|
||||
|
||||
} else if (角度信息 === "90") {
|
||||
|
||||
顺时针90旋转()
|
||||
} else {
|
||||
// 如果以上条件都不满足,则执行默认的代码
|
||||
}
|
||||
|
||||
|
||||
|
||||
//历史记录回退缩放函数()
|
||||
}
|
||||
app.activeDocument = 主文档;
|
||||
历史记录回退缩放函数()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
app.activeDocument = currentDocument;
|
||||
烧花线添加()//alert("当前码拍好")///////////////////////////////////这里可以填写添加烧花线函数
|
||||
|
||||
}
|
||||
//alert("排版完成,请检查文件!!!")
|
||||
app.activeDocument = 主文档;
|
||||
}
|
||||
|
||||
|
||||
// 将像素转换为毫米
|
||||
function pixelsToMillimeters(pixels) {
|
||||
// 获取当前文档
|
||||
var doc = app.activeDocument;
|
||||
|
||||
// 获取图像的分辨率(像素/英寸)
|
||||
var resolution = doc.resolution;
|
||||
|
||||
// 计算像素转换为毫米
|
||||
var inches = pixels / resolution;
|
||||
var millimeters = inches * 25.4;
|
||||
|
||||
return millimeters.toFixed(2); // 保留两位小数
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 顺时针90旋转() //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d1);
|
||||
d.putUnitDouble(stringIDToTypeID("angle"), stringIDToTypeID("angleUnit"), 90);
|
||||
d.putBoolean(stringIDToTypeID("linked"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 逆时针90旋转() //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d1);
|
||||
d.putUnitDouble(stringIDToTypeID("angle"), stringIDToTypeID("angleUnit"), -90);
|
||||
d.putBoolean(stringIDToTypeID("linked"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 匹配图层名(搜索词) {
|
||||
// 获取指定图层组中的所有图层
|
||||
function 获取组中所有图层(组) {
|
||||
var 图层数组 = [];
|
||||
var 图层组中图层 = 组.layers;
|
||||
|
||||
for (var i = 0; i < 图层组中图层.length; i++) {
|
||||
var 图层 = 图层组中图层[i];
|
||||
图层数组.push(图层);
|
||||
if (图层.typename === "LayerSet") {
|
||||
var 子图层 = 获取组中所有图层(图层);
|
||||
图层数组 = 图层数组.concat(子图层);
|
||||
}
|
||||
}
|
||||
|
||||
return 图层数组;
|
||||
}
|
||||
|
||||
// 获取指定名称的图层组
|
||||
function 根据名称获取图层组(文档, 组名称) {
|
||||
var 组 = null;
|
||||
var 所有图层 = 文档.layers;
|
||||
|
||||
for (var i = 0; i < 所有图层.length; i++) {
|
||||
var 图层 = 所有图层[i];
|
||||
if (图层.typename === "LayerSet" && 图层.name === 组名称) {
|
||||
组 = 图层;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 组;
|
||||
}
|
||||
|
||||
var 文档 = app.activeDocument;
|
||||
var 组名称 = "图层基础信息"; // 指定要匹配的图层组名称
|
||||
var 组 = 根据名称获取图层组(文档, 组名称);
|
||||
|
||||
if (组) {
|
||||
var 图层数组 = 获取组中所有图层(组);
|
||||
var 模糊匹配图层数组 = [];
|
||||
|
||||
// 首先进行模糊匹配
|
||||
for (var i = 0; i < 图层数组.length; i++) {
|
||||
var 图层 = 图层数组[i];
|
||||
if (图层.name.indexOf(搜索词) !== -1) {
|
||||
模糊匹配图层数组.push(图层);
|
||||
}
|
||||
}
|
||||
|
||||
// 在模糊匹配结果中进行图层基础信息数组分割过滤
|
||||
var 精确匹配图层数组 = [];
|
||||
for (var j = 0; j < 模糊匹配图层数组.length; j++) {
|
||||
var 模糊匹配图层 = 模糊匹配图层数组[j];
|
||||
// 进行图层基础信息数组分割过滤
|
||||
var 图层基础信息数组 = 模糊匹配图层.name.split("_"); // 假设分割符是 "_"
|
||||
if (图层基础信息数组[0] === 搜索词) {
|
||||
精确匹配图层数组.push(模糊匹配图层);
|
||||
}
|
||||
}
|
||||
|
||||
// 返回匹配的图层数组
|
||||
return 精确匹配图层数组;
|
||||
} else {
|
||||
alert('未找到名为"' + 组名称 + '"的图层组。');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 毫米转像素(毫米, 每英寸像素数) {
|
||||
var 每英寸毫米数 = 25.4;
|
||||
var 英寸 = 毫米 / 每英寸毫米数;
|
||||
return Math.round(英寸 * 每英寸像素数);
|
||||
}
|
||||
|
||||
function 图层按照缩放定位点进行宽高缩放(缩放定位点的X轴坐标,缩放定位点的Y轴坐标,缩放比例高度,缩放比例宽度) //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSIndependent"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 缩放定位点的X轴坐标);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 缩放定位点的Y轴坐标);
|
||||
d.putObject(stringIDToTypeID("position"), stringIDToTypeID("point"), d1);
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d2.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d2.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d2);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("percentUnit"), 缩放比例宽度);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("percentUnit"), 缩放比例高度);
|
||||
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 裁切图层(裁切上边距,裁切左边距,裁切下边距,裁切右边距) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("top"), stringIDToTypeID("pixelsUnit"), 裁切上边距);
|
||||
d1.putUnitDouble(stringIDToTypeID("left"), stringIDToTypeID("pixelsUnit"), 裁切左边距);
|
||||
d1.putUnitDouble(stringIDToTypeID("bottom"), stringIDToTypeID("pixelsUnit"),裁切下边距);
|
||||
d1.putUnitDouble(stringIDToTypeID("right"), stringIDToTypeID("pixelsUnit"), 裁切右边距);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("rectangle"), d1);
|
||||
d.putUnitDouble(stringIDToTypeID("angle"), stringIDToTypeID("angleUnit"), 0);
|
||||
d.putBoolean(stringIDToTypeID("delete"), true);
|
||||
d.putEnumerated(stringIDToTypeID("cropAspectRatioModeKey"), stringIDToTypeID("cropAspectRatioModeClass"), stringIDToTypeID("pureAspectRatio"));
|
||||
d.putBoolean(stringIDToTypeID("constrainProportions"), false);
|
||||
executeAction(stringIDToTypeID("crop"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 获取当前缩放定位点选区四边距() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var selectionBounds = currentDocument.selection.bounds;
|
||||
|
||||
var top2 = selectionBounds[1].value;
|
||||
var left2 = selectionBounds[0].value;
|
||||
var bottom2 = selectionBounds[3].value;
|
||||
var right2 = selectionBounds[2].value;
|
||||
|
||||
return {
|
||||
top2: top2,
|
||||
left2: left2,
|
||||
bottom2: bottom2,
|
||||
right2: right2
|
||||
};
|
||||
}
|
||||
|
||||
function 获取当前选区四边距() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var selectionBounds = currentDocument.selection.bounds;
|
||||
|
||||
var top = selectionBounds[1].value;
|
||||
var left = selectionBounds[0].value;
|
||||
var bottom = selectionBounds[3].value;
|
||||
var right = selectionBounds[2].value;
|
||||
|
||||
return {
|
||||
top: top,
|
||||
left: left,
|
||||
bottom: bottom,
|
||||
right: right
|
||||
};
|
||||
}
|
||||
|
||||
function 历史记录回退缩放函数() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putOffset(stringIDToTypeID("historyState"), -4 );
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 粘贴图层() //粘贴图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("paste"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 复制图层() //复制图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("copyEvent"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 取消选择() //取消选择
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("to"), stringIDToTypeID("ordinal"), stringIDToTypeID("none"));
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 图层选择(当前图层名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), 当前图层名称);
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(6);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 自由变换() //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d1);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("percentUnit"), -100);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("percentUnit"), -100);
|
||||
d.putBoolean(stringIDToTypeID("linked"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 选择上一图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(8);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 添加图层蒙版() //添加图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("at"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 应用图层蒙版() //应用图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("apply"), true);
|
||||
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 拼合所有蒙版() //拼合所有蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("e805a6ee-6d75-4b62-b6fe-f5873b5fdf20"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 选择蒙版() //选择蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 历史记录回退() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putOffset(stringIDToTypeID("historyState"), -5);
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 烧花线添加() {
|
||||
|
||||
app.activeDocument.suspendHistory("烧花线添加", "烧花线()");
|
||||
|
||||
|
||||
function 烧花线() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 遍历当前文档图层
|
||||
|
||||
var doc = app.activeDocument;
|
||||
var layers = doc.layers;
|
||||
var filteredLayers = [];
|
||||
|
||||
// 遍历图层,筛选以P开头的图层
|
||||
for (var i = 0; i < layers.length; i++) {
|
||||
var layer = layers[i];
|
||||
if (layer.name.charAt(0) === 'P') {
|
||||
filteredLayers.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
空置图层()
|
||||
// 输出图层名称
|
||||
for (var j = 0; j < filteredLayers.length; j++) {
|
||||
var filteredLayer = filteredLayers[j];
|
||||
|
||||
var 裁片底图名称=filteredLayer.name;
|
||||
|
||||
多选图层(裁片底图名称);
|
||||
|
||||
// alert(filteredLayer.name);
|
||||
|
||||
}
|
||||
合并图层();
|
||||
置为顶层();
|
||||
画布大小();
|
||||
var layer = app.activeDocument.activeLayer;
|
||||
layer.name = "底图";
|
||||
恢复默认颜色()
|
||||
矩形选框像素点()
|
||||
//色彩范围()
|
||||
填充();
|
||||
魔棒烧花线()
|
||||
新建图层()
|
||||
var layer2 = app.activeDocument.activeLayer;
|
||||
layer2.name = "剪口";
|
||||
扩展2();
|
||||
恢复止口线默认颜色()
|
||||
填充();
|
||||
矩形选框准备删除()
|
||||
清除();
|
||||
魔棒();
|
||||
扩展();
|
||||
选择反向();
|
||||
清除();
|
||||
var 底图 = app.activeDocument.layers.getByName( "底图");
|
||||
app.activeDocument.activeLayer=底图;
|
||||
矩形选框准备删除()
|
||||
清除();
|
||||
置为底层()
|
||||
图层样式()
|
||||
取消选择()
|
||||
|
||||
function 多选图层(裁片底图名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), 裁片底图名称);
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(4);
|
||||
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 空置图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("selectNoLayers"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 恢复止口线默认颜色() //取消选择
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("foregroundColor"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putDouble(stringIDToTypeID("cyan"), 20);
|
||||
d1.putDouble(stringIDToTypeID("magenta"), 0);
|
||||
d1.putDouble(stringIDToTypeID("yellowColor"), 0);
|
||||
d1.putDouble(stringIDToTypeID("black"), 0);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("CMYKColorClass"), d1);
|
||||
d.putString(stringIDToTypeID("source"), "photoshopPicker");
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 合并图层() //合并图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 恢复默认颜色() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("reset"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 魔棒烧花线() //魔棒
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("point"), d1);
|
||||
d.putInteger(stringIDToTypeID("tolerance"), 6);
|
||||
d.putBoolean(stringIDToTypeID("contiguous"), false);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
function 矩形选框像素点() //矩形选框
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("top"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("left"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("bottom"), stringIDToTypeID("distanceUnit"), 0.48);
|
||||
d1.putUnitDouble(stringIDToTypeID("right"), stringIDToTypeID("distanceUnit"), 0.48);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("rectangle"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 置为底层() //置为底层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("back"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 置为顶层() //置为顶层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("front"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 色彩范围() //色彩范围
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putInteger(stringIDToTypeID("fuzziness"), 40);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putDouble(stringIDToTypeID("luminance"), 0);
|
||||
d1.putDouble(stringIDToTypeID("a"), 0);
|
||||
d1.putDouble(stringIDToTypeID("b"), 0);
|
||||
d.putObject(stringIDToTypeID("minimum"), stringIDToTypeID("labColor"), d1);
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putDouble(stringIDToTypeID("luminance"), 0);
|
||||
d2.putDouble(stringIDToTypeID("a"), 0);
|
||||
d2.putDouble(stringIDToTypeID("b"), 0);
|
||||
d.putObject(stringIDToTypeID("maximum"), stringIDToTypeID("labColor"), d2);
|
||||
d.putInteger(stringIDToTypeID("colorModel"), 0);
|
||||
executeAction(stringIDToTypeID("colorRange"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 新建图层() //新建图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("layer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putInteger(stringIDToTypeID("layerID"), 33);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 扩展2() //扩展
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putUnitDouble(stringIDToTypeID("by"), stringIDToTypeID("pixelsUnit"), 1);
|
||||
d.putBoolean(stringIDToTypeID("selectionModifyEffectAtCanvasBounds"), false);
|
||||
executeAction(stringIDToTypeID("expand"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 填充() //填充
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("fillContents"), stringIDToTypeID("foregroundColor"));
|
||||
d.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||
d.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||
executeAction(stringIDToTypeID("fill"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 画布大小() //画布大小
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 40);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 40);
|
||||
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 魔棒() //魔棒
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 3);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 3);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("point"), d1);
|
||||
d.putInteger(stringIDToTypeID("tolerance"), 6);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 矩形选框准备删除() //矩形选框
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("top"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("left"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("bottom"), stringIDToTypeID("distanceUnit"), 0.96);
|
||||
d1.putUnitDouble(stringIDToTypeID("right"), stringIDToTypeID("distanceUnit"), 0.96);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("rectangle"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 扩展() //扩展
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putUnitDouble(stringIDToTypeID("by"), stringIDToTypeID("pixelsUnit"), 25);
|
||||
d.putBoolean(stringIDToTypeID("selectionModifyEffectAtCanvasBounds"), false);
|
||||
executeAction(stringIDToTypeID("expand"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 选择反向() //选择反向
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("inverse"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 清除() //清除
|
||||
{
|
||||
app.activeDocument.selection.clear();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 图层样式() //图层样式
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("layerEffects"));
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("scale"), stringIDToTypeID("percentUnit"), 12);
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putBoolean(stringIDToTypeID("enabled"), true);
|
||||
d2.putBoolean(stringIDToTypeID("present"), true);
|
||||
d2.putBoolean(stringIDToTypeID("showInDialog"), true);
|
||||
d2.putEnumerated(stringIDToTypeID("style"), stringIDToTypeID("frameStyle"), stringIDToTypeID("outsetFrame"));
|
||||
d2.putEnumerated(stringIDToTypeID("paintType"), stringIDToTypeID("frameFill"), stringIDToTypeID("solidColor"));
|
||||
d2.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||
d2.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||
d2.putUnitDouble(stringIDToTypeID("size"), stringIDToTypeID("pixelsUnit"), 16);
|
||||
var d3 = new ActionDescriptor();
|
||||
d3.putDouble(stringIDToTypeID("red"), 255);
|
||||
d3.putDouble(stringIDToTypeID("green"), 0);
|
||||
d3.putDouble(stringIDToTypeID("blue"), 0);
|
||||
d2.putObject(stringIDToTypeID("color"), stringIDToTypeID("RGBColor"), d3);
|
||||
d2.putBoolean(stringIDToTypeID("overprint"), false);
|
||||
d1.putObject(stringIDToTypeID("frameFX"), stringIDToTypeID("frameFX"), d2);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layerEffects"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 取消选择() //取消选择
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("to"), stringIDToTypeID("ordinal"), stringIDToTypeID("none"));
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
"""
|
||||
1187
temp_backup/Server_redundant/tempdemo/psmark/JSX14.py
Normal file
385
temp_backup/Server_redundant/tempdemo/psmark/JSX15.py
Normal file
@@ -0,0 +1,385 @@
|
||||
dxf15_jscode = """
|
||||
|
||||
|
||||
|
||||
function 图层自动编组2() {
|
||||
|
||||
app.activeDocument.suspendHistory("图层自动编组", "图层自动编组()");
|
||||
}
|
||||
|
||||
|
||||
function 图层自动编组(){
|
||||
var currentDocument = app.activeDocument;
|
||||
var matchCount = 0; // 匹配到的数值计数
|
||||
var existingPatternSet = false;
|
||||
var layerNames = []; // 保存匹配到的图层名称的数组
|
||||
|
||||
// 遍历图层
|
||||
for (var j = 0; j < currentDocument.layers.length; j++) {
|
||||
var layer = currentDocument.layers[j];
|
||||
var layerName = layer.name;
|
||||
|
||||
// 检查图层名称是否以P开头并且后面跟着数字
|
||||
if (/^P\d+$/.test(layerName)) {
|
||||
matchCount++;
|
||||
layerNames.push(layer); // 将匹配到的图层添加到数组中
|
||||
}
|
||||
}
|
||||
|
||||
// 输出匹配到的数值个数
|
||||
$.writeln("匹配到的数值个数:" + matchCount);
|
||||
|
||||
// 遍历匹配到的图层名称
|
||||
for (var i = 0; i < layerNames.length; i++) {
|
||||
var layerName = layerNames[i].name;
|
||||
// $.writeln("匹配到的图层名称:" + layerName);
|
||||
var 当前花样图层 = app.activeDocument.layers.getByName(layerName);
|
||||
app.activeDocument.activeLayer = 当前花样图层;
|
||||
图层编组main()
|
||||
app.activeDocument.activeLayer.name=layerName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 图层编组main() //图层编组
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("layerSection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("from"), r1);
|
||||
d.putInteger(stringIDToTypeID("layerSectionStart"), 43);
|
||||
d.putInteger(stringIDToTypeID("layerSectionEnd"), 44);
|
||||
d.putString(stringIDToTypeID("name"), "main");
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 快速超级链接2() {
|
||||
|
||||
app.activeDocument.suspendHistory("快速超链接", "快速超级链接()");
|
||||
}
|
||||
|
||||
|
||||
function 快速超级链接() {
|
||||
var 导出目录 = Folder.selectDialog("选择超链接素材目录");
|
||||
if (!导出目录) {
|
||||
alert("未选择导出目录。操作已取消。");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
画布大小()
|
||||
app.preferences.rulerUnits = Units.PIXELS
|
||||
var currentDocument = app.activeDocument;
|
||||
var matchCount = 0; // 匹配到的数值计数
|
||||
var existingPatternSet = false;
|
||||
var layerNames = []; // 保存匹配到的图层名称的数组
|
||||
|
||||
// 遍历图层
|
||||
for (var j = 0; j < currentDocument.layers.length; j++) {
|
||||
var layer = currentDocument.layers[j];
|
||||
var layerName = layer.name;
|
||||
|
||||
// 检查图层名称是否以P开头并且后面跟着数字
|
||||
if (/^P\d+$/.test(layerName)) {
|
||||
matchCount++;
|
||||
layerNames.push(layer); // 将匹配到的图层添加到数组中
|
||||
}
|
||||
}
|
||||
|
||||
// 输出匹配到的数值个数
|
||||
// $.writeln("匹配到的数值个数:" + matchCount);
|
||||
|
||||
// 遍历匹配到的图层名称
|
||||
for (var i = 0; i < layerNames.length; i++) {
|
||||
var layerName = layerNames[i].name;
|
||||
$.writeln("匹配到的图层名称:" + layerName);
|
||||
var 当前花样图层 = app.activeDocument.layers.getByName(layerName);
|
||||
app.activeDocument.activeLayer = 当前花样图层;
|
||||
切换mask()
|
||||
载入选区()
|
||||
var 边距 = 获取当前选区四边距();
|
||||
var 毫米 = 130;
|
||||
var 每英寸像素数 = app.activeDocument.resolution; // 获取当前文档的分辨率(每英寸像素数)
|
||||
var 扩展像素 = 毫米转像素(毫米, 每英寸像素数);
|
||||
|
||||
var 裁切上边距= 边距.top-扩展像素
|
||||
var 裁切左边距= 边距.left-扩展像素
|
||||
var 裁切下边距= 边距.bottom+扩展像素
|
||||
var 裁切右边距= 边距.right+扩展像素
|
||||
|
||||
var selRegion = [
|
||||
[裁切左边距,裁切上边距],
|
||||
[裁切右边距,裁切上边距],
|
||||
[裁切右边距,裁切下边距],
|
||||
[裁切左边距,裁切下边距]
|
||||
];
|
||||
|
||||
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||
新建图层()
|
||||
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||
|
||||
var c = new SolidColor();
|
||||
c.rgb.hexValue = "FFFFFF";
|
||||
app.activeDocument.selection.fill(c);
|
||||
后移一层()
|
||||
app.activeDocument.activeLayer = 当前花样图层;
|
||||
切换mask()
|
||||
载入选区()
|
||||
删除图层蒙版()
|
||||
创建剪贴蒙版()
|
||||
向下合并()
|
||||
新建文档()
|
||||
当前花样图层 = app.activeDocument.activeLayer
|
||||
app.activeDocument.crop( 当前花样图层.bounds, 0);
|
||||
var 文件路径 = 导出目录 + "/" + layerName + ".tif";
|
||||
tiffOptions = new TiffSaveOptions();
|
||||
app.activeDocument.saveAs(new File(文件路径), tiffOptions);
|
||||
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
app.activeDocument=currentDocument
|
||||
转换为智能对象()
|
||||
重新链接到文件(文件路径)
|
||||
添加图层蒙版()
|
||||
//当前图层=app.activeDocument.activeLayer
|
||||
//当前图层.name=layerName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 新建文档() //复制图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("document"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("using"), r1);
|
||||
d.putInteger(stringIDToTypeID("version"), 5);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
function 转换为智能对象() //转换为智能对象
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("newPlacedLayer"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 重新链接到文件(文件路径) //重新链接到文件
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putPath(stringIDToTypeID("null"), new File(文件路径));
|
||||
d.putInteger(stringIDToTypeID("layerID"), 94);
|
||||
executeAction(stringIDToTypeID("placedLayerRelinkToFile"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 切换mask() //取消选择
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 毫米转像素(毫米, 每英寸像素数) {
|
||||
var 每英寸毫米数 = 25.4;
|
||||
var 英寸 = 毫米 / 每英寸毫米数;
|
||||
return Math.round(英寸 * 每英寸像素数);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 画布大小() //画布大小
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 850.56);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 850.56);
|
||||
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 获取当前选区四边距() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var selectionBounds = currentDocument.selection.bounds;
|
||||
|
||||
var top = selectionBounds[1].value;
|
||||
var left = selectionBounds[0].value;
|
||||
var bottom = selectionBounds[3].value;
|
||||
var right = selectionBounds[2].value;
|
||||
|
||||
return {
|
||||
top: top,
|
||||
left: left,
|
||||
bottom: bottom,
|
||||
right: right
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 新建图层() //新建图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("layer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putInteger(stringIDToTypeID("layerID"), 135);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 后移一层() //后移一层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 删除图层蒙版() //删除图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 删除图层蒙版() //删除图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 向下合并() //向下合并
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 添加图层蒙版() //添加图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("at"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 创建剪贴蒙版() //创建剪贴蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("groupEvent"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
587
temp_backup/Server_redundant/tempdemo/psmark/JSX16.py
Normal file
@@ -0,0 +1,587 @@
|
||||
dxf16_jscode = """
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 快速定位码链接() {
|
||||
|
||||
app.activeDocument.suspendHistory("快速定位码链接", "花样图层导出()");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 花样图层导出() {
|
||||
var 导出目录 = Folder.selectDialog("选择外链素材目录");
|
||||
if (!导出目录) {
|
||||
alert("未选择导出目录。操作已取消。");
|
||||
return;
|
||||
}
|
||||
花样图层导出为TIF透明底(导出目录)
|
||||
// 花样图层导出为TIF(导出目录);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 花样图层导出为TIF透明底(导出目录) {
|
||||
|
||||
app.preferences.rulerUnits = Units.MM;
|
||||
var doc = app.activeDocument;
|
||||
var 扩展毫米数=80
|
||||
|
||||
// 获取文档中的所有图层
|
||||
var allLayers = doc.layers;
|
||||
|
||||
var 名称数组 = [];
|
||||
// 循环遍历所有图层
|
||||
for (var i = 0; i < allLayers.length; i++) {
|
||||
// 检查图层是否是图层组
|
||||
if (allLayers[i] instanceof LayerSet) {
|
||||
// 获取图层组中的所有子图层
|
||||
subLayers = allLayers[i].layers;
|
||||
图层组名称=allLayers[i].name
|
||||
文档名称=app.activeDocument.name
|
||||
文档名称去除后缀 = 文档名称.replace(/\.[^\.]+$/, "");
|
||||
// 检查图层组是否包含子图层
|
||||
if (subLayers.length > 0) {
|
||||
// 获取图层组中最后一个子图层的名称
|
||||
var lastSubLayer = subLayers[subLayers.length - 1];
|
||||
var lastSubLayerName = lastSubLayer.name;
|
||||
var lastSubLayerName = lastSubLayer.name;
|
||||
FastSubLayer = subLayers[0];
|
||||
FastSubLayername= FastSubLayer.name
|
||||
|
||||
//alert(SastSubLayerName)
|
||||
// 输出图层组名称和最后一个子图层的名称
|
||||
|
||||
|
||||
if (图层组名称 === "填充底图") {
|
||||
for (var j = 0; j < subLayers.length; j++) {
|
||||
// alert(subLayers[j].name);
|
||||
填充底图组内部循环名称 = subLayers[j].name
|
||||
填充底图循环素材 = app.activeDocument.layerSets.getByName("填充底图").layers.getByName(填充底图组内部循环名称);
|
||||
app.activeDocument.activeLayer = 填充底图循环素材;
|
||||
新建文档()
|
||||
// 合并图层()
|
||||
载入选区()
|
||||
裁剪()
|
||||
名称=文档名称去除后缀+"-("+填充底图组内部循环名称 +")-填充底图"
|
||||
名称数组.push('"' + 名称 + '"');
|
||||
制作图案预设(名称)
|
||||
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
app.activeDocument=doc
|
||||
// 执行某一个操作(例如,设置图层组的可见性)
|
||||
// alert("测试")
|
||||
}
|
||||
|
||||
|
||||
// 手动创建JSON格式的字符串
|
||||
var jsonStr = '[' + 名称数组.join(', ') + ']';
|
||||
|
||||
// 创建一个文件对象指向桌面
|
||||
|
||||
var file = new File(导出目录 + "/名称数据.json");
|
||||
|
||||
// 打开文件,写入JSON字符串,然后关闭文件
|
||||
file.open('w');
|
||||
file.write(jsonStr);
|
||||
file.close();
|
||||
|
||||
} else {
|
||||
// 执行别的操作(例如,隐藏其他图层组)
|
||||
|
||||
|
||||
|
||||
空白裁片模板 = app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(lastSubLayerName);
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
新建图层()
|
||||
app.activeDocument.activeLayer.name="最大白边值"
|
||||
裁片边界 = lastSubLayer.bounds;
|
||||
扩展值 = 毫米转像素(扩展毫米数); //50cm
|
||||
裁片边界_左 = 毫米转像素(裁片边界[0]) - 扩展值;
|
||||
|
||||
裁片边界_上 = 毫米转像素(裁片边界[1]) - 扩展值;
|
||||
裁片边界_右 = 毫米转像素(裁片边界[2]) + 扩展值;
|
||||
裁片边界_下 = 毫米转像素(裁片边界[3]) + 扩展值;
|
||||
|
||||
var selRegion = [
|
||||
[裁片边界_左,裁片边界_上],
|
||||
[裁片边界_右,裁片边界_上],
|
||||
[裁片边界_右,裁片边界_下],
|
||||
[裁片边界_左,裁片边界_下]
|
||||
];
|
||||
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||
背景切换();
|
||||
恢复白底();
|
||||
填充();
|
||||
隐藏图层();
|
||||
裁片图层组 = app.activeDocument.layerSets.getByName(图层组名称)
|
||||
app.activeDocument.activeLayer = 裁片图层组 ;
|
||||
新建文档()
|
||||
空白裁片模板 = app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(lastSubLayerName);
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
删除图层()
|
||||
最大白边值=app.activeDocument.layerSets.getByName(图层组名称).layers.getByName("最大白边值");
|
||||
app.activeDocument.activeLayer = 最大白边值;
|
||||
app.activeDocument.crop(最大白边值.bounds, 0);
|
||||
|
||||
// 保存为TIF
|
||||
var 文件路径 = 导出目录 + "/" + 图层组名称 + ".tif";
|
||||
tiffOptions = new TiffSaveOptions();
|
||||
app.activeDocument.saveAs(new File(文件路径), tiffOptions);
|
||||
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
app.activeDocument=doc
|
||||
最大白边值=app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(FastSubLayername);
|
||||
app.activeDocument.activeLayer = 最大白边值;
|
||||
//上移图层()
|
||||
多选图层()
|
||||
转换为智能对象()
|
||||
释放剪贴蒙版()
|
||||
栅格化图层()
|
||||
选择下一图层()
|
||||
新建图层()
|
||||
背景切换();
|
||||
恢复白底();
|
||||
填充();
|
||||
选择上一图层()
|
||||
添加图层蒙版()
|
||||
向下合并()
|
||||
转换为智能对象()
|
||||
重新链接到文件(文件路径)
|
||||
创建剪贴蒙版()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
function 恢复白底() //删除图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("exchange"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 创建剪贴蒙版() //创建剪贴蒙版
|
||||
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("groupEvent"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 制作图案预设(名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("pattern"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("using"), r1);
|
||||
d.putString(stringIDToTypeID("name"), 名称);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function mergeLayersNew_72799682617188() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 合并图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 背景切换() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("reset"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 图层可见性show() //图层可见性
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var list = new ActionList();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
list.putReference(r);
|
||||
d.putList(stringIDToTypeID("null"), list);
|
||||
executeAction(stringIDToTypeID("show"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 裁剪() //裁剪
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putBoolean(stringIDToTypeID("delete"), true);
|
||||
executeAction(stringIDToTypeID("crop"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 重新链接到文件(文件路径) //重新链接到文件
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putPath(stringIDToTypeID("null"), new File(文件路径));
|
||||
d.putInteger(stringIDToTypeID("layerID"), 94);
|
||||
executeAction(stringIDToTypeID("placedLayerRelinkToFile"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 转换为智能对象() //转换为智能对象
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("newPlacedLayer"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 向下合并() //向下合并
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 添加图层蒙版() //添加图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("at"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function 上移图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(11);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 毫米转像素(毫米)
|
||||
{
|
||||
//厘米转像素
|
||||
doc_w = app.activeDocument.width;
|
||||
//用户设定的厘米数 支持小数
|
||||
user_mm = UnitValue(毫米,"mm");
|
||||
user_px = user_mm.as("px")*app.activeDocument.resolution/72;
|
||||
return user_px;
|
||||
}
|
||||
|
||||
|
||||
function 删除图层() //删除图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var list = new ActionList();
|
||||
list.putInteger(22);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 多选图层(SastSubLayerName) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), "最大白边值");
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelectionContinuous"));
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
|
||||
list.putInteger(115);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
function 新建图层() //新建图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("layer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putBoolean(stringIDToTypeID("group"), true);
|
||||
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("layer"), d1);
|
||||
d.putInteger(stringIDToTypeID("layerID"), 22);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 释放剪贴蒙版() //释放剪贴蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("ungroup"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 添加图层蒙版() //添加图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("at"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 向下合并() //向下合并
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 栅格化图层() //栅格化图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("rasterizeLayer"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 选择下一图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("backwardEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(314);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 选择上一图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(369);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
function 新建图层() //新建图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("layer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putInteger(stringIDToTypeID("layerID"), 373);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 恢复白底() //删除图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("exchange"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 背景切换() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("reset"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 填充() //填充
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("fillContents"), stringIDToTypeID("foregroundColor"));
|
||||
d.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||
d.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||
executeAction(stringIDToTypeID("fill"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 填充() //填充
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("fillContents"), stringIDToTypeID("foregroundColor"));
|
||||
d.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||
d.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||
executeAction(stringIDToTypeID("fill"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
function 隐藏图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var list = new ActionList();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
list.putReference(r);
|
||||
d.putList(stringIDToTypeID("null"), list);
|
||||
executeAction(stringIDToTypeID("hide"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 新建文档() //复制图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("document"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("using"), r1);
|
||||
d.putInteger(stringIDToTypeID("version"), 5);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
1008
temp_backup/Server_redundant/tempdemo/psmark/JSX17.py
Normal file
284
temp_backup/Server_redundant/tempdemo/psmark/JSX18.py
Normal file
@@ -0,0 +1,284 @@
|
||||
dxf18_jscode = """
|
||||
|
||||
function 龙服的快速换图(){
|
||||
// 强制使用 UTF-8 编码
|
||||
#target photoshop
|
||||
$.localize = true;
|
||||
|
||||
// 创建对话框
|
||||
var dialog = new Window("dialog");
|
||||
dialog.text = "快速换图特定版本";
|
||||
dialog.orientation = "column";
|
||||
dialog.alignChildren = ["left","top"];
|
||||
dialog.spacing = 10;
|
||||
dialog.margins = 16;
|
||||
|
||||
// 大货模板文件夹选择面板
|
||||
var templatePanel = dialog.add("panel", undefined, "大货模板文件夹选择");
|
||||
templatePanel.orientation = "row";
|
||||
templatePanel.alignChildren = ["left","center"];
|
||||
templatePanel.spacing = 10;
|
||||
templatePanel.margins = 10;
|
||||
|
||||
// 大货模板文件夹路径文本框
|
||||
var templatePathEditText = templatePanel.add('edittext', undefined, '', { properties: { readonly: true } });
|
||||
templatePathEditText.preferredSize.width = 300;
|
||||
|
||||
// 大货模板路径选择按钮
|
||||
var selectTemplateButton = templatePanel.add("button", undefined, "选择文件夹");
|
||||
selectTemplateButton.onClick = function() {
|
||||
var selectedFolder = Folder.selectDialog("选择大货模板文件夹");
|
||||
if (selectedFolder != null) {
|
||||
templatePathEditText.text = selectedFolder.fsName;
|
||||
// alert( templatePathEditText.text)
|
||||
updateFileNames(selectedFolder);
|
||||
}
|
||||
};
|
||||
|
||||
// 切片裁片文件夹选择面板
|
||||
var slicePanel = dialog.add("panel", undefined, "切片裁片文件夹选择");
|
||||
slicePanel.orientation = "row";
|
||||
slicePanel.alignChildren = ["left","center"];
|
||||
slicePanel.spacing = 10;
|
||||
slicePanel.margins = 10;
|
||||
|
||||
// 切片裁片文件夹路径文本框
|
||||
var slicePathEditText = slicePanel.add('edittext', undefined, '', { properties: { readonly: true } });
|
||||
slicePathEditText.preferredSize.width = 300;
|
||||
|
||||
// 切片裁片路径选择按钮
|
||||
var selectSliceButton = slicePanel.add("button", undefined, "选择文件夹");
|
||||
selectSliceButton.onClick = function() {
|
||||
var selectedFolder = Folder.selectDialog("选择切片裁片文件夹");
|
||||
if (selectedFolder != null) {
|
||||
slicePathEditText.text = selectedFolder.fsName;
|
||||
// 可以在这里执行切片裁片相关的操作
|
||||
}
|
||||
};
|
||||
|
||||
// 大货裁片名称面板
|
||||
var fileNamesPanel = dialog.add("panel", undefined, "大货裁片名称数量");
|
||||
fileNamesPanel.orientation = "column";
|
||||
fileNamesPanel.alignChildren = ["left","top"];
|
||||
fileNamesPanel.spacing = 10;
|
||||
fileNamesPanel.margins = 10;
|
||||
|
||||
// 存储文件名和输入框内容的数组
|
||||
var userInputData = [];
|
||||
|
||||
// 更新文件名和输入框显示
|
||||
function updateFileNames(folder) {
|
||||
// 移除之前的所有元素
|
||||
for (var i = fileNamesPanel.children.length - 1; i >= 0; i--) {
|
||||
fileNamesPanel.children[i].remove();
|
||||
}
|
||||
|
||||
// 清空数组
|
||||
userInputData = [];
|
||||
|
||||
var files = folder.getFiles();
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
// 使用正则表达式提取文件名(去掉路径和后缀)
|
||||
完整文件路径=files[i].fsName
|
||||
var fileName = new File(files[i]).name.replace(/\.\w+$/, "");
|
||||
|
||||
// 创建新的静态文本框
|
||||
var fileNameStaticText = fileNamesPanel.add('statictext', undefined, fileName);
|
||||
fileNameStaticText.preferredSize.width = 200;
|
||||
|
||||
// 创建新的输入框
|
||||
var inputEditText = fileNamesPanel.add('edittext', undefined, '');
|
||||
inputEditText.preferredSize.width = 100;
|
||||
|
||||
// 存储文件名和输入框内容
|
||||
userInputData.push({
|
||||
fileName: fileName,
|
||||
inputText: ''
|
||||
});
|
||||
}
|
||||
|
||||
// 重新绘制对话框
|
||||
dialog.layout.layout(true);
|
||||
dialog.layout.resize();
|
||||
}
|
||||
|
||||
// OK 和 Cancel 按钮
|
||||
var buttonsGroup = dialog.add("group");
|
||||
buttonsGroup.orientation = "row";
|
||||
buttonsGroup.alignChildren = ["fill","top"];
|
||||
buttonsGroup.spacing = 10;
|
||||
buttonsGroup.margins = 0;
|
||||
|
||||
var okButton = buttonsGroup.add("button", undefined, "执行");
|
||||
okButton.onClick = function() {
|
||||
// 在这里执行 OK 按钮的操作
|
||||
updateUserData();
|
||||
alertUserInput();
|
||||
dialog.close();
|
||||
};
|
||||
|
||||
var cancelButton = buttonsGroup.add("button", undefined, "取消");
|
||||
cancelButton.onClick = function() {
|
||||
// 在这里执行 Cancel 按钮的操作
|
||||
dialog.close();
|
||||
};
|
||||
|
||||
// 显示对话框
|
||||
dialog.show();
|
||||
|
||||
// 更新用户输入数据
|
||||
function updateUserData() {
|
||||
for (var i = 1; i < fileNamesPanel.children.length; i += 2) {
|
||||
userInputData[(i - 1) / 2].inputText = fileNamesPanel.children[i].text;
|
||||
}
|
||||
}
|
||||
// 弹出用户输入的内容
|
||||
function alertUserInput() {
|
||||
var userInput = "";
|
||||
for (var i = 0; i < userInputData.length; i++) {
|
||||
var 文件路径=templatePathEditText.text
|
||||
var 文件名=userInputData[i].fileName
|
||||
var 完整文件路径=文件路径+"/"+文件名+".tif"
|
||||
// alert( 完整文件路径)
|
||||
var 文件数量= userInputData[i].inputText
|
||||
var 文件夹路径=slicePathEditText.text
|
||||
var 文件对象 = new File(完整文件路径);
|
||||
if (文件对象.exists) {
|
||||
app.open(文件对象);
|
||||
} else {
|
||||
//alert("文件不存在:" + 完整文件路径);
|
||||
}
|
||||
|
||||
更换当前文档裁片组外链(文件夹路径)
|
||||
图层选择()
|
||||
activeDocument.activeLayer.textItem.contents=文件数量
|
||||
选择裁片图层()
|
||||
合并图层()
|
||||
另存为(文件夹路径)
|
||||
|
||||
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
alert("换图完成,请检查好文件进行打印大货!!!",dialog.text+"----关于");
|
||||
|
||||
dialog.close();
|
||||
|
||||
|
||||
|
||||
function 另存为(文件夹路径)
|
||||
{
|
||||
文档名称=activeDocument.name.replace(/(?:\.[^.]*$|$)/, '');
|
||||
saveIn=File(文件夹路径+ "/"+文档名称);
|
||||
tifSaveOpt = new TiffSaveOptions();
|
||||
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||
asCopy=true
|
||||
app.activeDocument.saveAs(saveIn,tifSaveOpt,asCopy);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 选择裁片图层() //
|
||||
{
|
||||
try {
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), "裁片");
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(74);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
}
|
||||
catch (e) {
|
||||
alert("找不到裁片图层",dialog.text+"----关于");
|
||||
|
||||
}
|
||||
}
|
||||
function 合并图层() //合并图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
function 更换当前文档裁片组外链(文件夹路径)
|
||||
{
|
||||
try
|
||||
{
|
||||
裁片组 = app.activeDocument.layerSets.getByName("裁片").layers;
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
alert("找不到裁片组",dialog.text+"----提示");
|
||||
|
||||
}
|
||||
|
||||
for(var i=0;i<裁片组.length;i++)
|
||||
{
|
||||
裁片 = 裁片组[i];
|
||||
app.activeDocument.activeLayer = 裁片;
|
||||
if(裁片.kind == LayerKind.SMARTOBJECT)
|
||||
{
|
||||
更换链接智能对象路径(文件夹路径);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function 更换链接智能对象路径(文件夹路径)
|
||||
{
|
||||
//获取当前图层外链的智能对象路径
|
||||
//先获取链接的文件名
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
//~ r.putName(charIDToTypeID("Lyr "), "◆左袖口"); //按名称查找
|
||||
descLayer = executeActionGet(r);
|
||||
res = descLayer.getObjectValue(stringIDToTypeID("smartObject"));
|
||||
|
||||
链接文件名 = res.getString(stringIDToTypeID("fileReference"));
|
||||
//$.writeln(链接文件名);
|
||||
|
||||
//~ 链接文件路径 = res.getPath(stringIDToTypeID("link"));
|
||||
//~ $.writeln(链接文件路径);
|
||||
|
||||
图片路径 = 文件夹路径 + "/" + 链接文件名;
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putPath(stringIDToTypeID("null"), new File(图片路径));
|
||||
executeAction(stringIDToTypeID("placedLayerRelinkToFile"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 图层选择() //
|
||||
{
|
||||
try {
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), "数量");
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(74);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
}
|
||||
catch (e) {
|
||||
alert("找不到数量图层",dialog.text+"----关于");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
"""
|
||||
166
temp_backup/Server_redundant/tempdemo/psmark/JSX19.py
Normal file
@@ -0,0 +1,166 @@
|
||||
dxf19_jscode = """
|
||||
|
||||
// 文件夹路径
|
||||
|
||||
function 裁片抓取新的() {
|
||||
app.activeDocument.suspendHistory("裁片抓取", "置入对象()");
|
||||
}
|
||||
|
||||
function 置入对象() {
|
||||
var folderPath = "D:/MarkTemp/Pdfmarktemp";
|
||||
|
||||
|
||||
var folder = new Folder(folderPath);
|
||||
|
||||
// 获取文件夹中的文件
|
||||
var files = folder.getFiles();
|
||||
|
||||
// 新建一个数组来存储文件名称
|
||||
var fileNamesArray = [];
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
if (files[i] instanceof File) {
|
||||
var doc = app.activeDocument;
|
||||
var fileName = files[i].name;
|
||||
var newDocumentName = fileName.replace(/\.pdf$/, '');
|
||||
var modifiedString = newDocumentName.replace(/-\d+_\d+/, '');
|
||||
var newmodifiedString = modifiedString.split("-");
|
||||
|
||||
// 检查是否存在相同的文件名
|
||||
var duplicate = false;
|
||||
for (var j = 0; j < fileNamesArray.length; j++) {
|
||||
if (fileNamesArray[j] === modifiedString) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicate) {
|
||||
$.writeln("文件名重复,跳过: " + fileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 将新的文档名添加到文件名数组中
|
||||
fileNamesArray.push(modifiedString);
|
||||
$.writeln("文档名称: " + fileName);
|
||||
|
||||
var 文件路径 = folderPath + "/" + fileName;
|
||||
|
||||
// 调用你的函数
|
||||
var match = newDocumentName.match(/_(\d+)/);
|
||||
var 角度信息 = match ? match[1] : "";
|
||||
|
||||
if (角度信息 === "180" || 角度信息 === "-180") {
|
||||
置入对象180度(文件路径);
|
||||
} else {
|
||||
// 如果角度信息不是 "180" 或 "-180",执行默认逻辑
|
||||
置入对象0度(文件路径);
|
||||
}
|
||||
|
||||
var extractedPart = newmodifiedString[0];
|
||||
var 大货裁片组名 = extractedPart + "-大货裁片";
|
||||
|
||||
// var 当前花样图层 = app.activeDocument.layers.getByName(newDocumentName);
|
||||
// app.activeDocument.activeLayer = 当前花样图层;
|
||||
|
||||
var 大货裁片组;
|
||||
try {
|
||||
大货裁片组 = app.activeDocument.layerSets.getByName(大货裁片组名);
|
||||
} catch (e) {
|
||||
大货裁片组 = app.activeDocument.layerSets.add();
|
||||
大货裁片组.name = 大货裁片组名;
|
||||
}
|
||||
|
||||
// 将当前图层移动到大货裁片图层组内
|
||||
app.activeDocument.activeLayer.move(大货裁片组, ElementPlacement.INSIDE);
|
||||
栅格化图层智能对象();
|
||||
// modifiedString = fileName.replace(/-\d+_\d+/, '');
|
||||
app.activeDocument.activeLayer.name = modifiedString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function 栅格化图层智能对象() {
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("rasterizeLayer"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 栅格化图层智能对象() //栅格化图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("rasterizeLayer"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 置入对象0度(文件路径)
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putEnumerated(stringIDToTypeID("selection"), stringIDToTypeID("pdfSelection"), stringIDToTypeID("page"));
|
||||
d1.putInteger(stringIDToTypeID("pageNumber"), 1);
|
||||
d1.putEnumerated(stringIDToTypeID("crop"), stringIDToTypeID("cropTo"), stringIDToTypeID("boundingBox"));
|
||||
d1.putBoolean(stringIDToTypeID("suppressWarnings"), false);
|
||||
d1.putBoolean(stringIDToTypeID("antiAlias"), true);
|
||||
d1.putBoolean(stringIDToTypeID("clippingPath"), true);
|
||||
d.putObject(stringIDToTypeID("as"), stringIDToTypeID("PDFGenericFormat"), d1);
|
||||
d.putInteger(stringIDToTypeID("ID"), 4);
|
||||
d.putPath(stringIDToTypeID("null"), new File(文件路径));
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d2.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d2);
|
||||
//d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("percentUnit"), -100);
|
||||
// d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("percentUnit"), -100);
|
||||
d.putBoolean(stringIDToTypeID("antiAlias"), false);
|
||||
executeAction(stringIDToTypeID("placeEvent"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 置入对象180度(文件路径)
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putEnumerated(stringIDToTypeID("selection"), stringIDToTypeID("pdfSelection"), stringIDToTypeID("page"));
|
||||
d1.putInteger(stringIDToTypeID("pageNumber"), 1);
|
||||
d1.putEnumerated(stringIDToTypeID("crop"), stringIDToTypeID("cropTo"), stringIDToTypeID("boundingBox"));
|
||||
d1.putBoolean(stringIDToTypeID("suppressWarnings"), false);
|
||||
d1.putBoolean(stringIDToTypeID("antiAlias"), true);
|
||||
d1.putBoolean(stringIDToTypeID("clippingPath"), true);
|
||||
d.putObject(stringIDToTypeID("as"), stringIDToTypeID("PDFGenericFormat"), d1);
|
||||
d.putInteger(stringIDToTypeID("ID"), 4);
|
||||
d.putPath(stringIDToTypeID("null"), new File(文件路径));
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d2.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d2);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("percentUnit"), -100);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("percentUnit"), -100);
|
||||
d.putBoolean(stringIDToTypeID("antiAlias"), false);
|
||||
executeAction(stringIDToTypeID("placeEvent"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
1910
temp_backup/Server_redundant/tempdemo/psmark/JSX2.py
Normal file
136
temp_backup/Server_redundant/tempdemo/psmark/JSX20.py
Normal file
@@ -0,0 +1,136 @@
|
||||
dxf20_jscode = """
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 混排通码延申导出() {
|
||||
var 主文档 = app.activeDocument;
|
||||
var 主文档路径 = 主文档.path;
|
||||
var 新文件夹 = (主文档路径 + "/小片裁片"); // 在桌面上创建一个名为"导出目录"的文件夹
|
||||
var 导出目录 = Folder(新文件夹);
|
||||
if (!导出目录.exists) {
|
||||
导出目录.create();
|
||||
}
|
||||
|
||||
var doc = app.activeDocument;
|
||||
|
||||
// 获取所有图层
|
||||
var layers = doc.layers;
|
||||
|
||||
// 存储符合条件的图层组
|
||||
var matchingLayerSets = [];
|
||||
|
||||
// 遍历每个图层
|
||||
for (var i = 0; i < layers.length; i++) {
|
||||
var layer = layers[i];
|
||||
|
||||
// 如果是图层组,检查名称是否包含 "大货裁片"
|
||||
if (layer.typename == "LayerSet" && layer.name.indexOf("大货裁片") !== -1) {
|
||||
matchingLayerSets.push(layer);
|
||||
|
||||
// 输出图层组名称
|
||||
$.writeln("图层组名称:" + layer.name);
|
||||
图层组名称=layer.name
|
||||
// 输出图层组内子图层的名称
|
||||
for (var j = 0; j < layer.layers.length; j++) {
|
||||
var subLayer = layer.layers[j];
|
||||
$.writeln(" 子图层名称:" + subLayer.name);
|
||||
子图层名称=subLayer.name
|
||||
子图层名称分割=子图层名称.split("-")
|
||||
素材图名称=子图层名称分割[0]
|
||||
空白裁片模板 = app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(子图层名称);
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
载入选区()
|
||||
素材名称 = app.activeDocument.layers.getByName(素材图名称);
|
||||
app.activeDocument.activeLayer = 素材名称;
|
||||
添加图层蒙版()
|
||||
新建文档()
|
||||
当前花样图层 = app.activeDocument.activeLayer
|
||||
app.activeDocument.crop( 当前花样图层.bounds, 0);
|
||||
var 文件路径 = 导出目录 + "/" + 子图层名称 + ".tif";
|
||||
tiffOptions = new TiffSaveOptions();
|
||||
app.activeDocument.saveAs(new File(文件路径), tiffOptions);
|
||||
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
app.activeDocument=doc
|
||||
|
||||
历史记录回退3()
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 输出符合条件的图层组数量
|
||||
//$.writeln("符合条件的图层组数量:" + matchingLayerSets.length);
|
||||
|
||||
}
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 添加图层蒙版() //添加图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("at"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 新建文档() //复制图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("document"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("using"), r1);
|
||||
d.putInteger(stringIDToTypeID("version"), 5);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
function 历史记录回退3() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putOffset(stringIDToTypeID("historyState"), -2);
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
39
temp_backup/Server_redundant/tempdemo/psmark/JSX21.py
Normal file
@@ -0,0 +1,39 @@
|
||||
dxf21_jscode = """
|
||||
|
||||
function 创建裁片排版文档(画布宽,画布高,分辨率,文档名称)
|
||||
{
|
||||
app.preferences.rulerUnits = Units.MM; //修改指定单位为毫米
|
||||
app.documents.add(画布宽, 画布高,分辨率, 文档名称, NewDocumentMode.CMYK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 置入链接的智能对象(dir, DXFname) {
|
||||
图片路径 = dir + "/" + DXFname + ".tif"
|
||||
var d = new ActionDescriptor();
|
||||
d.putInteger(stringIDToTypeID("ID"), 16);
|
||||
d.putPath(stringIDToTypeID("null"), new File(图片路径));
|
||||
d.putBoolean(stringIDToTypeID("linked"), true);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d1 = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("placeEvent"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
function 裁片排版_lay(中心x_mm, 中心y_mm) {
|
||||
ps中心x_mm = 中心x_mm;
|
||||
ps中心y_mm = 中心y_mm;
|
||||
|
||||
alb = app.activeDocument.activeLayer.bounds;
|
||||
当前x = (alb[0] + alb[2]) / 2; //mm数
|
||||
当前y = (alb[1] + alb[3]) / 2; //mm数
|
||||
|
||||
app.activeDocument.activeLayer.translate(ps中心x_mm - Number(当前x), ps中心y_mm - Number(当前y)); //全局单位设置为mm即可
|
||||
}
|
||||
|
||||
function 裁片角度(角度) {
|
||||
app.activeDocument.activeLayer.rotate(角度);
|
||||
//app.refresh()
|
||||
}
|
||||
|
||||
"""
|
||||
453
temp_backup/Server_redundant/tempdemo/psmark/JSX22.py
Normal file
@@ -0,0 +1,453 @@
|
||||
dxf22_jscode = """
|
||||
|
||||
|
||||
//PS智能对象换图
|
||||
|
||||
function 模特换衣功能(){
|
||||
|
||||
|
||||
var dialog = new Window("dialog");
|
||||
dialog.text = "模特批量替换";
|
||||
dialog.preferredSize.width = 400;
|
||||
dialog.preferredSize.height = 150;
|
||||
dialog.orientation = "column";
|
||||
dialog.alignChildren = ["center","top"];
|
||||
dialog.spacing = 10;
|
||||
dialog.margins = 16;
|
||||
|
||||
// GROUP1
|
||||
// ======
|
||||
var group1 = dialog.add("group");
|
||||
group1.orientation = "row";
|
||||
group1.alignChildren = ["left","center"];
|
||||
group1.spacing = 10;
|
||||
group1.margins = 0;
|
||||
|
||||
var statictext1 = group1.add("statictext");
|
||||
statictext1.text = "模板文件";
|
||||
statictext1.justify = "center";
|
||||
|
||||
var edittext1 = group1.add("edittext");
|
||||
edittext1.preferredSize.width = 250;
|
||||
|
||||
var button1 = group1.add("button");
|
||||
button1.text = "选择文件";
|
||||
button1.justify = "center";
|
||||
|
||||
// GROUP2
|
||||
// ======
|
||||
var group2 = dialog.add("group");
|
||||
group2.orientation = "row";
|
||||
group2.alignChildren = ["left","center"];
|
||||
group2.spacing = 10;
|
||||
group2.margins = 0;
|
||||
|
||||
var statictext2 = group2.add("statictext");
|
||||
statictext2.text = "素材目录";
|
||||
statictext2.justify = "center";
|
||||
|
||||
var edittext2 = group2.add("edittext");
|
||||
edittext2.preferredSize.width = 250;
|
||||
|
||||
var button2 = group2.add("button");
|
||||
button2.text = "选择目录";
|
||||
button2.justify = "center";
|
||||
|
||||
// GROUP3
|
||||
// ======
|
||||
var group3 = dialog.add("group");
|
||||
group3.orientation = "row";
|
||||
group3.alignChildren = ["left","center"];
|
||||
group3.spacing = 10;
|
||||
group3.margins = 0;
|
||||
|
||||
var statictext3 = group3.add("statictext");
|
||||
statictext3.text = "导出目录";
|
||||
statictext3.justify = "center";
|
||||
|
||||
var edittext3 = group3.add("edittext");
|
||||
edittext3.preferredSize.width = 250;
|
||||
|
||||
var button3 = group3.add("button");
|
||||
button3.text = "选择目录";
|
||||
button3.justify = "center";
|
||||
|
||||
|
||||
var group5 = dialog.add("group");
|
||||
var cbox1 = group5.add("checkbox");
|
||||
cbox1.text = "是否遍历子文件夹素材";
|
||||
cbox1.value = true;
|
||||
|
||||
var cbox2 = group5.add("checkbox");
|
||||
cbox2.text = "是否保存文件结构";
|
||||
cbox2.value = true;
|
||||
|
||||
var cbox3 = group5.add("checkbox");
|
||||
cbox3.text = "切片导出";
|
||||
cbox3.value = false;
|
||||
|
||||
// GROUP4
|
||||
// ======
|
||||
var group4 = dialog.add("group");
|
||||
group4.orientation = "row";
|
||||
group4.alignChildren = ["left","center"];
|
||||
group4.spacing = 10;
|
||||
group4.margins = 0;
|
||||
|
||||
var button4 = group4.add("button");
|
||||
button4.text = "执行";
|
||||
button4.justify = "center";
|
||||
|
||||
var button5 = group4.add("button");
|
||||
button5.text = "退出";
|
||||
button5.justify = "center";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
button1.onClick = function()
|
||||
{
|
||||
|
||||
var inputFile= app.openDialog();
|
||||
if (inputFile != null)
|
||||
{
|
||||
edittext1.text = decodeURI(inputFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
button2.onClick = function()
|
||||
{
|
||||
|
||||
var inputFolder = Folder.selectDialog("请选择素材目录:");
|
||||
if (inputFolder != null)
|
||||
{
|
||||
edittext2.text = decodeURI(inputFolder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
button3.onClick = function()
|
||||
{
|
||||
|
||||
var inputFolder = Folder.selectDialog("请选择导出目录:");
|
||||
if (inputFolder != null)
|
||||
{
|
||||
edittext3.text = decodeURI(inputFolder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
button4.onClick = function()
|
||||
{
|
||||
模板路径 = edittext1.text;
|
||||
素材目录 = edittext2.text;
|
||||
导出目录 = edittext3.text;
|
||||
main(模板路径,素材目录,导出目录);
|
||||
}
|
||||
|
||||
button5.onClick = function()
|
||||
{
|
||||
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
|
||||
function main(模板路径,素材目录,导出目录)
|
||||
{
|
||||
|
||||
|
||||
if(Folder(导出目录).exists==false){Folder(导出目录).create();}
|
||||
|
||||
var doc = app.open(File(模板路径));
|
||||
//~ 素材文件列表 = Folder(素材目录).getFiles("*.psd");
|
||||
|
||||
isSubFolders = cbox1.value;
|
||||
素材文件列表 = 遍历目录指定类型文件(素材目录,isSubFolders);
|
||||
|
||||
|
||||
alert("当前目录一共有"+素材文件列表.length+"个素材。","提示:");
|
||||
|
||||
try
|
||||
{
|
||||
lay_替换对象图层名 = "替换对象";
|
||||
lay_替换对象 = app.activeDocument.artLayers.getByName(lay_替换对象图层名);
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
alert("未找到["+lay_替换对象图层名+"]智能对象图层!","提示:");
|
||||
}
|
||||
|
||||
app.activeDocument.activeLayer = lay_替换对象;
|
||||
|
||||
for(var i=0;i<素材文件列表.length;i++)
|
||||
{
|
||||
scpsd_path = 素材文件列表[i];
|
||||
//~ lay_替换对象.visible = false;
|
||||
|
||||
素材名 = decodeURI(File(scpsd_path).name.replace(/(?:\.[^.]*$|$)/, ''));
|
||||
|
||||
//替换内容
|
||||
var d = new ActionDescriptor();
|
||||
d.putPath(stringIDToTypeID("null"), new File(scpsd_path));
|
||||
executeAction(stringIDToTypeID("placedLayerReplaceContents"), d, DialogModes.NO);
|
||||
|
||||
if(cbox2.value) //保持结构
|
||||
{
|
||||
结构导出目录 = 导出目录+"/" + getRelativePath(decodeURI(File(scpsd_path).path), 素材目录);
|
||||
|
||||
if(Folder(结构导出目录).exists==false){Folder(结构导出目录).create();}
|
||||
|
||||
保存路径 = 结构导出目录+"/"+素材名+".jpg";
|
||||
$.writeln(保存路径);
|
||||
|
||||
if(cbox3.value) //按切片
|
||||
{
|
||||
//~ 按切片导出图片(结构导出目录,app.activeDocument.name.replace(/(?:\.[^.]*$|$)/, ''));
|
||||
按切片导出图片(结构导出目录,素材名);
|
||||
}
|
||||
else
|
||||
{
|
||||
保存JPG(保存路径);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(cbox3.value) //按切片
|
||||
{
|
||||
//~ 按切片导出图片(导出目录,app.activeDocument.name.replace(/(?:\.[^.]*$|$)/, ''));
|
||||
按切片导出图片(导出目录,素材名);
|
||||
}
|
||||
else
|
||||
{
|
||||
保存JPG(导出目录+"/"+素材名+".jpg");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
doc.close(SaveOptions.DONOTSAVECHANGES);
|
||||
alert("处理完成!","提示:");
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getRelativePath(targetPath, basePath)
|
||||
{
|
||||
var targetFile = new File(targetPath);
|
||||
var baseFolder = new Folder(basePath);
|
||||
|
||||
var relativePath = targetFile.getRelativeURI(baseFolder);
|
||||
|
||||
return decodeURI(relativePath); // 解码 URI 编码的路径
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 保存JPG(jpg_save_path)
|
||||
{
|
||||
// 以JPEG格式保存输出
|
||||
var jpegOptions = new JPEGSaveOptions();
|
||||
// 将jpeg质量设置得很低,使文件很小
|
||||
jpegOptions.quality = 12;
|
||||
|
||||
app.activeDocument.saveAs(new File(jpg_save_path), jpegOptions,true);
|
||||
}
|
||||
|
||||
|
||||
function 遍历目录指定类型文件(inputFolder,isSubFolders)
|
||||
{
|
||||
if(isSubFolders==undefined)
|
||||
{
|
||||
isSubFolders = true;
|
||||
}
|
||||
|
||||
all_files_list = [];
|
||||
if (inputFolder != null) {
|
||||
|
||||
filesArray = scanFolder(inputFolder);
|
||||
|
||||
if (filesArray.length > 0)
|
||||
{
|
||||
for (i = 0;i<filesArray.length;i++)
|
||||
{
|
||||
all_files_list.push(filesArray[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return all_files_list;
|
||||
|
||||
function scanFolder(folder)
|
||||
{
|
||||
|
||||
var filesArray = [],
|
||||
|
||||
fileList = Folder(folder).getFiles();
|
||||
var file;
|
||||
|
||||
|
||||
|
||||
for (var i = 0; i < fileList.length; i++) {
|
||||
|
||||
file = fileList[i];
|
||||
|
||||
if (file instanceof Folder)
|
||||
{
|
||||
if(isSubFolders==false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
filesArray = filesArray.concat(scanFolder(file));
|
||||
}
|
||||
|
||||
}
|
||||
else if (file instanceof File && file.name.match(/\.(jpg|tif|psd|png|)$/i))
|
||||
{
|
||||
|
||||
filesArray.push(file);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return filesArray;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 按切片导出图片(保存路径,替换词)
|
||||
{
|
||||
//~ alert(保存路径);
|
||||
var d = new ActionDescriptor();
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putEnumerated(charIDToTypeID("Op "), charIDToTypeID("SWOp"), charIDToTypeID("OpSa"));
|
||||
d1.putBoolean(charIDToTypeID("DIDr"), true);
|
||||
|
||||
d1.putPath(stringIDToTypeID("in"), new File(保存路径));
|
||||
|
||||
d1.putString(charIDToTypeID("ovFN"), 替换词+".jpg");
|
||||
|
||||
|
||||
d1.putEnumerated(stringIDToTypeID("format"), charIDToTypeID("IRFm"), stringIDToTypeID("JPEG"));
|
||||
d1.putBoolean(charIDToTypeID("Intr"), false);
|
||||
d1.putInteger(stringIDToTypeID("quality"), 80);
|
||||
d1.putInteger(charIDToTypeID("QChS"), 0);
|
||||
d1.putInteger(charIDToTypeID("QCUI"), 0);
|
||||
d1.putBoolean(charIDToTypeID("QChT"), false);
|
||||
d1.putBoolean(charIDToTypeID("QChV"), false);
|
||||
d1.putBoolean(stringIDToTypeID("optimized"), true);
|
||||
d1.putInteger(charIDToTypeID("Pass"), 1);
|
||||
d1.putDouble(stringIDToTypeID("blur"), 0);
|
||||
d1.putBoolean(charIDToTypeID("Mtt "), true);
|
||||
d1.putBoolean(charIDToTypeID("EICC"), false);
|
||||
d1.putInteger(charIDToTypeID("MttR"), 255);
|
||||
d1.putInteger(charIDToTypeID("MttG"), 255);
|
||||
d1.putInteger(charIDToTypeID("MttB"), 255);
|
||||
d1.putBoolean(charIDToTypeID("SHTM"), false);
|
||||
d1.putBoolean(charIDToTypeID("SImg"), true);
|
||||
d1.putEnumerated(charIDToTypeID("SWsl"), charIDToTypeID("STsl"), charIDToTypeID("SLAl"));
|
||||
d1.putEnumerated(charIDToTypeID("SWch"), charIDToTypeID("STch"), charIDToTypeID("CHDc"));
|
||||
d1.putEnumerated(charIDToTypeID("SWmd"), charIDToTypeID("STmd"), charIDToTypeID("MDCC"));
|
||||
d1.putBoolean(charIDToTypeID("ohXH"), false);
|
||||
d1.putBoolean(charIDToTypeID("ohIC"), true);
|
||||
d1.putBoolean(charIDToTypeID("ohAA"), true);
|
||||
d1.putBoolean(charIDToTypeID("ohQA"), true);
|
||||
d1.putBoolean(charIDToTypeID("ohCA"), false);
|
||||
d1.putBoolean(charIDToTypeID("ohIZ"), true);
|
||||
d1.putEnumerated(charIDToTypeID("ohTC"), charIDToTypeID("SToc"), charIDToTypeID("OC03"));
|
||||
d1.putEnumerated(charIDToTypeID("ohAC"), charIDToTypeID("SToc"), charIDToTypeID("OC03"));
|
||||
d1.putInteger(charIDToTypeID("ohIn"), -1);
|
||||
d1.putEnumerated(charIDToTypeID("ohLE"), charIDToTypeID("STle"), charIDToTypeID("LE03"));
|
||||
d1.putEnumerated(charIDToTypeID("ohEn"), charIDToTypeID("STen"), charIDToTypeID("EN00"));
|
||||
d1.putBoolean(charIDToTypeID("olCS"), false);
|
||||
d1.putEnumerated(charIDToTypeID("olEC"), charIDToTypeID("STst"), charIDToTypeID("ST00"));
|
||||
d1.putEnumerated(charIDToTypeID("olWH"), charIDToTypeID("STwh"), charIDToTypeID("WH01"));
|
||||
d1.putEnumerated(charIDToTypeID("olSV"), charIDToTypeID("STsp"), charIDToTypeID("SP04"));
|
||||
d1.putEnumerated(charIDToTypeID("olSH"), charIDToTypeID("STsp"), charIDToTypeID("SP04"));
|
||||
var list = new ActionList();
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC00"));
|
||||
list.putObject(charIDToTypeID("SCnc"), d2);
|
||||
var d3 = new ActionDescriptor();
|
||||
d3.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC19"));
|
||||
list.putObject(charIDToTypeID("SCnc"), d3);
|
||||
var d4 = new ActionDescriptor();
|
||||
d4.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC28"));
|
||||
list.putObject(charIDToTypeID("SCnc"), d4);
|
||||
var d5 = new ActionDescriptor();
|
||||
d5.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC24"));
|
||||
list.putObject(charIDToTypeID("SCnc"), d5);
|
||||
var d6 = new ActionDescriptor();
|
||||
d6.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC24"));
|
||||
list.putObject(charIDToTypeID("SCnc"), d6);
|
||||
var d7 = new ActionDescriptor();
|
||||
d7.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC24"));
|
||||
list.putObject(charIDToTypeID("SCnc"), d7);
|
||||
d1.putList(charIDToTypeID("olNC"), list);
|
||||
d1.putBoolean(charIDToTypeID("obIA"), false);
|
||||
d1.putString(charIDToTypeID("obIP"), "");
|
||||
d1.putEnumerated(charIDToTypeID("obCS"), charIDToTypeID("STcs"), charIDToTypeID("CS01"));
|
||||
var list1 = new ActionList();
|
||||
var d8 = new ActionDescriptor();
|
||||
d8.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC01"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d8);
|
||||
var d9 = new ActionDescriptor();
|
||||
d9.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC20"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d9);
|
||||
var d10 = new ActionDescriptor();
|
||||
d10.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC02"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d10);
|
||||
var d11 = new ActionDescriptor();
|
||||
d11.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC19"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d11);
|
||||
var d12 = new ActionDescriptor();
|
||||
d12.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC06"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d12);
|
||||
var d13 = new ActionDescriptor();
|
||||
d13.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC24"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d13);
|
||||
var d14 = new ActionDescriptor();
|
||||
d14.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC24"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d14);
|
||||
var d15 = new ActionDescriptor();
|
||||
d15.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC24"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d15);
|
||||
var d16 = new ActionDescriptor();
|
||||
d16.putEnumerated(charIDToTypeID("ncTp"), charIDToTypeID("STnc"), charIDToTypeID("NC22"));
|
||||
list1.putObject(charIDToTypeID("SCnc"), d16);
|
||||
d1.putList(charIDToTypeID("ovNC"), list1);
|
||||
d1.putBoolean(charIDToTypeID("ovCM"), false);
|
||||
d1.putBoolean(charIDToTypeID("ovCW"), true);
|
||||
d1.putBoolean(charIDToTypeID("ovCU"), true);
|
||||
d1.putBoolean(charIDToTypeID("ovSF"), true);
|
||||
d1.putBoolean(charIDToTypeID("ovCB"), true);
|
||||
|
||||
|
||||
|
||||
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("SaveForWeb"), d1);
|
||||
executeAction(stringIDToTypeID("export"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
"""
|
||||
474
temp_backup/Server_redundant/tempdemo/psmark/JSX23.py
Normal file
@@ -0,0 +1,474 @@
|
||||
dxf23_jscode = """
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 自动连晒(){
|
||||
var dialog = new Window("dialog");
|
||||
|
||||
dialog.text = "S/O样自动连晒";
|
||||
//dialog.text = "SO自动米样拼贴";
|
||||
dialog.orientation = "row";
|
||||
dialog.alignChildren = ["left","top"];
|
||||
dialog.spacing = 10;
|
||||
dialog.margins = 16;
|
||||
|
||||
// GROUP1
|
||||
// ======
|
||||
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||
group1.orientation = "column";
|
||||
group1.alignChildren = ["fill","top"];
|
||||
group1.spacing = 10;
|
||||
group1.margins = 0;
|
||||
|
||||
// PANEL1
|
||||
// ======
|
||||
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||
panel1.text = "源图像";
|
||||
panel1.preferredSize.width = 388;
|
||||
panel1.preferredSize.height = 205;
|
||||
panel1.orientation = "column";
|
||||
panel1.alignChildren = ["left","top"];
|
||||
panel1.spacing = 10;
|
||||
panel1.margins = 10;
|
||||
|
||||
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||
statictext1.text = "使用:";
|
||||
|
||||
// GROUP2
|
||||
// ======
|
||||
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||
group2.orientation = "row";
|
||||
group2.alignChildren = ["left","center"];
|
||||
group2.spacing = 10;
|
||||
group2.margins = 0;
|
||||
|
||||
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||
button1.text = "选取";
|
||||
|
||||
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||
edittext1.text = "[选择图像文件夹]";
|
||||
edittext1.preferredSize.width = 300;
|
||||
|
||||
// PANEL2
|
||||
// ======
|
||||
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||
panel2.text = "SO小样文档";
|
||||
panel2.preferredSize.height = 160;
|
||||
panel2.orientation = "column";
|
||||
panel2.alignChildren = ["left","top"];
|
||||
panel2.spacing = 10;
|
||||
panel2.margins = 10;
|
||||
|
||||
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||
statictext2.text = "设定:";
|
||||
|
||||
// GROUP3
|
||||
// ======
|
||||
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||
group3.orientation = "row";
|
||||
group3.alignChildren = ["left","center"];
|
||||
group3.spacing = 10;
|
||||
group3.margins = 0;
|
||||
|
||||
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||
statictext3.text = "宽度(cm):";
|
||||
|
||||
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||
edittext2.preferredSize.width = 100;
|
||||
|
||||
var statictext4 = group3.add("statictext", undefined, undefined, {name: "statictext4"});
|
||||
statictext4.text = "高度(cm):";
|
||||
|
||||
var edittext3 = group3.add('edittext {properties: {name: "edittext3"}}');
|
||||
edittext3.preferredSize.width = 100;
|
||||
|
||||
// GROUP4
|
||||
// ======
|
||||
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||
group4.orientation = "row";
|
||||
group4.alignChildren = ["left","center"];
|
||||
group4.spacing = 10;
|
||||
group4.margins = 0;
|
||||
|
||||
// GROUP5
|
||||
// ======
|
||||
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||
group5.orientation = "row";
|
||||
group5.alignChildren = ["left","center"];
|
||||
group5.spacing = 10;
|
||||
group5.margins = 0;
|
||||
|
||||
// PANEL3
|
||||
// ======
|
||||
|
||||
|
||||
// GROUP7
|
||||
// ======
|
||||
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||
group7.orientation = "column";
|
||||
group7.alignChildren = ["fill","top"];
|
||||
group7.spacing = 10;
|
||||
group7.margins = 0;
|
||||
|
||||
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||
ok.text = "确认";
|
||||
|
||||
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||
cancel.text = "取消";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
button1.onClick = function () {
|
||||
// 打开文件夹选择对话框
|
||||
var selectedFolder = Folder.selectDialog("选择图像文件夹");
|
||||
|
||||
// 检查用户是否取消了选择
|
||||
if (selectedFolder) {
|
||||
// 将选择的文件夹路径显示在输入框中
|
||||
edittext1.text = selectedFolder.fsName;
|
||||
}
|
||||
};
|
||||
|
||||
ok.onClick = function () {
|
||||
app.preferences.rulerUnits = Units.PIXELS;
|
||||
var 素材图片文件夹 = new Folder(edittext1.text);
|
||||
var 遍历tiff = 素材图片文件夹.getFiles("*.*");
|
||||
|
||||
// 新建文件夹
|
||||
var 新文件夹 = new Folder(edittext1.text + "/SO小样拼贴");
|
||||
新文件夹.create();
|
||||
var 新加字文件夹 = new Folder(edittext1.text + "/SO小样拼贴加字");
|
||||
新加字文件夹.create();
|
||||
|
||||
|
||||
宽度=Number (edittext2.text);
|
||||
高度=Number (edittext3.text);
|
||||
|
||||
|
||||
for (var i = 0; i < 遍历tiff.length; i++) {
|
||||
if (遍历tiff[i] instanceof File) {
|
||||
app.open(遍历tiff[i]);
|
||||
app.activeDocument.flatten();
|
||||
var 当前文档 = app.activeDocument;
|
||||
var 当前文档名称 = 当前文档.name;
|
||||
图像大小()
|
||||
预设图案(当前文档名称);
|
||||
app.activeDocument.crop([UnitValue("0px"), UnitValue("0px"), UnitValue(宽度, "cm"), UnitValue(高度, "cm")]);
|
||||
填充图案(当前文档名称);
|
||||
app.activeDocument.flatten();
|
||||
画布扩展()
|
||||
// 保存新的图像文件到新建的文件夹中
|
||||
var 保存路径 = 新文件夹 + "/" + 当前文档名称;
|
||||
var tifSaveOpt = new TiffSaveOptions();
|
||||
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||
var asCopy = true;
|
||||
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||
创建并处理文本图层();
|
||||
app.activeDocument.flatten();
|
||||
var 保存路径 = 新加字文件夹 + "/" + 当前文档名称;
|
||||
var tifSaveOpt = new TiffSaveOptions();
|
||||
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||
var asCopy = true;
|
||||
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
//alert("小样连晒完成")
|
||||
dialog.close();
|
||||
// 创建并保存拼贴图像(新文件夹, 幅宽)
|
||||
};
|
||||
|
||||
|
||||
|
||||
function 预设图案(当前文档名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("pattern"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("using"), r1);
|
||||
d.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 填充图案(当前文档名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("contentLayer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
var d2 = new ActionDescriptor();
|
||||
var d3 = new ActionDescriptor();
|
||||
d3.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||
|
||||
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 图像大小() //图像大小
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), 200);
|
||||
d.putBoolean(stringIDToTypeID("scaleStyles"), true);
|
||||
d.putBoolean(stringIDToTypeID("constrainProportions"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("imageSize"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 画布扩展() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||
d.putEnumerated(stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("color"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putDouble(stringIDToTypeID("red"), 255);
|
||||
d1.putDouble(stringIDToTypeID("green"), 255);
|
||||
d1.putDouble(stringIDToTypeID("blue"), 255);
|
||||
d.putObject(stringIDToTypeID("canvasExtensionColor"), stringIDToTypeID("RGBColor"), d1);
|
||||
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 创建并处理文本图层() {
|
||||
// 新建图层
|
||||
var textLayer = activeDocument.artLayers.add();
|
||||
|
||||
// 将新建图层变成文本图层
|
||||
textLayer.kind = LayerKind.TEXT;
|
||||
|
||||
// 将文本内容改为当前文档name
|
||||
textLayer.textItem.contents = activeDocument.name;
|
||||
|
||||
// 字体大小固定值
|
||||
var 固定字体大小 = 30; // 例如,30像素
|
||||
textLayer.textItem.size = 固定字体大小;
|
||||
|
||||
// 文字字体固定值
|
||||
textLayer.textItem.font = "微软雅黑";
|
||||
|
||||
// 计算并调整文本位置
|
||||
var x = activeDocument.width - textLayer.bounds[2];
|
||||
var y = textLayer.bounds[1];
|
||||
textLayer.translate(x, -y);
|
||||
|
||||
// 偏移
|
||||
textLayer.translate(UnitValue("-1cm"), UnitValue("+0.5cm"));
|
||||
|
||||
// 复制并栅格化图层
|
||||
var copyLayer = textLayer.duplicate();
|
||||
copyLayer.rasterize(RasterizeType.ENTIRELAYER);
|
||||
|
||||
// 设置固定颜色值
|
||||
var 固定颜色 = new SolidColor();
|
||||
固定颜色.rgb.red = 255; // 红色分量
|
||||
固定颜色.rgb.green = 255; // 绿色分量
|
||||
固定颜色.rgb.blue = 255; // 蓝色分量
|
||||
|
||||
activeDocument.activeLayer = copyLayer;
|
||||
activeDocument.selection.fill(固定颜色, ColorBlendMode.NORMAL, 100, true);
|
||||
|
||||
// 白边
|
||||
activeDocument.activeLayer.applyMinimum(10);
|
||||
后移一层()
|
||||
向上选择()
|
||||
向下合并()
|
||||
名称更改字体()
|
||||
缩小字体图层至文档一半()
|
||||
多选背景()
|
||||
底对齐()
|
||||
水平居中对齐()
|
||||
}
|
||||
|
||||
// 调用函数
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 后移一层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 向上选择() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(19);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
function 向下合并() //向下合并
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 名称更改字体() //名称更改
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putString(stringIDToTypeID("name"), "字体");
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layer"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 缩小字体图层至文档一半() {
|
||||
var 文档 = app.activeDocument;
|
||||
var 字体图层 = null;
|
||||
|
||||
// 遍历文档中的图层以找到名为"字体"的图层
|
||||
for (var i = 0; i < 文档.artLayers.length; i++) {
|
||||
if (文档.artLayers[i].name === "字体") {
|
||||
字体图层 = 文档.artLayers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (字体图层 !== null) {
|
||||
// 获取文档的宽度的一半
|
||||
var 目标宽度 = 文档.width / 2;
|
||||
|
||||
// 获取图层的当前宽度
|
||||
var 图层宽度 = 字体图层.bounds[2] - 字体图层.bounds[0];
|
||||
|
||||
// 计算缩放比例
|
||||
var 缩放比例 = 目标宽度 / 图层宽度 * 100;
|
||||
|
||||
// 缩放图层
|
||||
字体图层.resize(缩放比例, 缩放比例, AnchorPosition.MIDDLECENTER);
|
||||
} else {
|
||||
alert("未找到名为'字体'的图层");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function 多选背景() //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), "背景");
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(1);
|
||||
list.putInteger(13);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 底对齐() //底对齐
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSBottoms"));
|
||||
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 水平居中对齐() //水平居中对齐
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSCentersH"));
|
||||
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 设置单位为像素
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
370
temp_backup/Server_redundant/tempdemo/psmark/JSX24.py
Normal file
@@ -0,0 +1,370 @@
|
||||
dxf24_jscode = """
|
||||
|
||||
|
||||
|
||||
// 设置单位为像素
|
||||
|
||||
function 自动米样拼贴(){
|
||||
|
||||
var dialog = new Window("dialog");
|
||||
|
||||
|
||||
dialog.text = "SO自动米样拼贴";
|
||||
dialog.orientation = "row";
|
||||
dialog.alignChildren = ["left","top"];
|
||||
dialog.spacing = 10;
|
||||
dialog.margins = 16;
|
||||
|
||||
// GROUP1
|
||||
// ======
|
||||
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||
group1.orientation = "column";
|
||||
group1.alignChildren = ["fill","top"];
|
||||
group1.spacing = 10;
|
||||
group1.margins = 0;
|
||||
|
||||
// PANEL1
|
||||
// ======
|
||||
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||
panel1.text = "源图像";
|
||||
panel1.preferredSize.width = 388;
|
||||
panel1.preferredSize.height = 205;
|
||||
panel1.orientation = "column";
|
||||
panel1.alignChildren = ["left","top"];
|
||||
panel1.spacing = 10;
|
||||
panel1.margins = 10;
|
||||
|
||||
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||
statictext1.text = "使用:";
|
||||
|
||||
// GROUP2
|
||||
// ======
|
||||
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||
group2.orientation = "row";
|
||||
group2.alignChildren = ["left","center"];
|
||||
group2.spacing = 10;
|
||||
group2.margins = 0;
|
||||
|
||||
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||
button1.text = "选取";
|
||||
|
||||
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||
edittext1.text = "[选择图像文件夹]";
|
||||
edittext1.preferredSize.width = 300;
|
||||
|
||||
// PANEL2
|
||||
// ======
|
||||
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||
panel2.text = "SO小样文档";
|
||||
panel2.preferredSize.height = 160;
|
||||
panel2.orientation = "column";
|
||||
panel2.alignChildren = ["left","top"];
|
||||
panel2.spacing = 10;
|
||||
panel2.margins = 10;
|
||||
|
||||
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||
statictext2.text = "设定:";
|
||||
|
||||
// GROUP3
|
||||
// ======
|
||||
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||
group3.orientation = "row";
|
||||
group3.alignChildren = ["left","center"];
|
||||
group3.spacing = 10;
|
||||
group3.margins = 0;
|
||||
|
||||
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||
statictext3.text = "幅宽(cm):";
|
||||
|
||||
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||
edittext2.preferredSize.width = 100;
|
||||
|
||||
|
||||
|
||||
// GROUP4
|
||||
// ======
|
||||
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||
group4.orientation = "row";
|
||||
group4.alignChildren = ["left","center"];
|
||||
group4.spacing = 10;
|
||||
group4.margins = 0;
|
||||
|
||||
// GROUP5
|
||||
// ======
|
||||
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||
group5.orientation = "row";
|
||||
group5.alignChildren = ["left","center"];
|
||||
group5.spacing = 10;
|
||||
group5.margins = 0;
|
||||
|
||||
// PANEL3
|
||||
// ======
|
||||
|
||||
|
||||
// GROUP7
|
||||
// ======
|
||||
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||
group7.orientation = "column";
|
||||
group7.alignChildren = ["fill","top"];
|
||||
group7.spacing = 10;
|
||||
group7.margins = 0;
|
||||
|
||||
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||
ok.text = "确认";
|
||||
|
||||
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||
cancel.text = "取消";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var dialog = new Window("dialog");
|
||||
dialog.text = "SO自动米样拼贴";
|
||||
dialog.orientation = "row";
|
||||
dialog.alignChildren = ["left","top"];
|
||||
dialog.spacing = 10;
|
||||
dialog.margins = 16;
|
||||
|
||||
// GROUP1
|
||||
// ======
|
||||
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||
group1.orientation = "column";
|
||||
group1.alignChildren = ["fill","top"];
|
||||
group1.spacing = 10;
|
||||
group1.margins = 0;
|
||||
|
||||
// PANEL1
|
||||
// ======
|
||||
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||
panel1.text = "源图像";
|
||||
panel1.preferredSize.width = 388;
|
||||
panel1.preferredSize.height = 205;
|
||||
panel1.orientation = "column";
|
||||
panel1.alignChildren = ["left","top"];
|
||||
panel1.spacing = 10;
|
||||
panel1.margins = 10;
|
||||
|
||||
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||
statictext1.text = "使用:";
|
||||
|
||||
// GROUP2
|
||||
// ======
|
||||
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||
group2.orientation = "row";
|
||||
group2.alignChildren = ["left","center"];
|
||||
group2.spacing = 10;
|
||||
group2.margins = 0;
|
||||
|
||||
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||
button1.text = "选取";
|
||||
|
||||
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||
edittext1.text = "[选择图像文件夹]";
|
||||
edittext1.preferredSize.width = 300;
|
||||
|
||||
// PANEL2
|
||||
// ======
|
||||
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||
panel2.text = "SO小样文档拼贴";
|
||||
panel2.preferredSize.height = 160;
|
||||
panel2.orientation = "column";
|
||||
panel2.alignChildren = ["left","top"];
|
||||
panel2.spacing = 10;
|
||||
panel2.margins = 10;
|
||||
|
||||
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||
statictext2.text = "设定:";
|
||||
|
||||
// GROUP3
|
||||
// ======
|
||||
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||
group3.orientation = "row";
|
||||
group3.alignChildren = ["left","center"];
|
||||
group3.spacing = 10;
|
||||
group3.margins = 0;
|
||||
|
||||
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||
statictext3.text = "幅宽(cm):";
|
||||
|
||||
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||
edittext2.preferredSize.width = 100;
|
||||
|
||||
|
||||
|
||||
|
||||
// GROUP4
|
||||
// ======
|
||||
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||
group4.orientation = "row";
|
||||
group4.alignChildren = ["left","center"];
|
||||
group4.spacing = 10;
|
||||
group4.margins = 0;
|
||||
|
||||
// GROUP5
|
||||
// ======
|
||||
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||
group5.orientation = "row";
|
||||
group5.alignChildren = ["left","center"];
|
||||
group5.spacing = 10;
|
||||
group5.margins = 0;
|
||||
|
||||
// PANEL3
|
||||
// ======
|
||||
|
||||
|
||||
// GROUP7
|
||||
// ======
|
||||
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||
group7.orientation = "column";
|
||||
group7.alignChildren = ["fill","top"];
|
||||
group7.spacing = 10;
|
||||
group7.margins = 0;
|
||||
|
||||
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||
ok.text = "确认";
|
||||
|
||||
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||
cancel.text = "取消";
|
||||
|
||||
|
||||
|
||||
|
||||
button1.onClick = function () {
|
||||
// 打开文件夹选择对话框
|
||||
var selectedFolder = Folder.selectDialog("选择图像文件夹");
|
||||
|
||||
// 检查用户是否取消了选择
|
||||
if (selectedFolder) {
|
||||
// 将选择的文件夹路径显示在输入框中
|
||||
edittext1.text = selectedFolder.fsName;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
ok.onClick = function () {
|
||||
app.preferences.rulerUnits = Units.PIXELS;
|
||||
//function 创建拼贴图(导入文件夹路径, 文档宽度厘米) {
|
||||
|
||||
// 假设这些值是从某处获取或者用户输入的
|
||||
var 导入文件夹路径 =new Folder (edittext1.text);
|
||||
var 文档宽度厘米 = Number(edittext2.text);
|
||||
var 文档高度厘米 = 300; // 例如,50厘米
|
||||
var 分辨率 = 200; // 分辨率设置为150 DPI
|
||||
|
||||
// 转换厘米到像素
|
||||
var 厘米到像素的转换系数 = 2.54;
|
||||
var 文档宽度像素 = 文档宽度厘米 / 厘米到像素的转换系数 * 分辨率;
|
||||
var 文档高度像素 = 文档高度厘米 / 厘米到像素的转换系数 * 分辨率;
|
||||
|
||||
var 导入文件夹 = new Folder(导入文件夹路径);
|
||||
var 文件列表 = 导入文件夹.getFiles("*.*");
|
||||
var 文件名数组 = new Array();
|
||||
var 文件宽度数组 = new Array();
|
||||
var 文件高度数组 = new Array();
|
||||
|
||||
|
||||
// 读取文件列表并获取文件信息
|
||||
for (var 索引1 = 0; 索引1 < 文件列表.length; 索引1++) {
|
||||
var 文档 = app.open(文件列表[索引1]);
|
||||
文件名数组[索引1] = 文档.fullName;
|
||||
文件宽度数组[索引1] = parseInt(文档.width);
|
||||
文件高度数组[索引1] = parseInt(文档.height);
|
||||
文档.close();
|
||||
}
|
||||
|
||||
// 根据高度对文件进行排序
|
||||
for (var 索引1 = 文件高度数组.length - 1; 索引1 > 0; --索引1) {
|
||||
for (var 索引2 = 0; 索引2 < 索引1; ++索引2) {
|
||||
if (文件高度数组[索引2] > 文件高度数组[索引2 + 1])
|
||||
交换数组元素(索引2, 索引2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
function 交换数组元素(索引1, 索引2) {
|
||||
var 临时 = 文件高度数组[索引1];
|
||||
文件高度数组[索引1] = 文件高度数组[索引2];
|
||||
文件高度数组[索引2] = 临时;
|
||||
|
||||
var 临时2 = 文件宽度数组[索引1];
|
||||
文件宽度数组[索引1] = 文件宽度数组[索引2];
|
||||
文件宽度数组[索引2] = 临时2;
|
||||
|
||||
var 临时3 = 文件名数组[索引1];
|
||||
文件名数组[索引1] = 文件名数组[索引2];
|
||||
文件名数组[索引2] = 临时3;
|
||||
}
|
||||
|
||||
// 创建新文档并添加图片
|
||||
var 新文档 = app.documents.add(文档宽度像素, 文档高度像素, 分辨率, "拼贴", NewDocumentMode.CMYK, DocumentFill.WHITE);
|
||||
var 当前顶部 = 0;
|
||||
var 当前左侧 = 0;
|
||||
for (var 索引1 = 0; 索引1 < 文件名数组.length; 索引1++) {
|
||||
var 文档 = app.open(文件名数组[索引1]);
|
||||
文档.selection.selectAll();
|
||||
文档.selection.copy();
|
||||
文档.close();
|
||||
|
||||
if ((当前左侧 + 文件宽度数组[索引1]) > 文档宽度像素) {
|
||||
当前左侧 = 0;
|
||||
当前顶部 += 文件高度数组[索引1 - 1];
|
||||
}
|
||||
|
||||
var 边界 = [
|
||||
[当前左侧, 当前顶部],
|
||||
[当前左侧 + 文件宽度数组[索引1], 当前顶部],
|
||||
[当前左侧 + 文件宽度数组[索引1], 当前顶部 + 文件高度数组[索引1]],
|
||||
[当前左侧, 当前顶部 + 文件高度数组[索引1]]
|
||||
];
|
||||
新文档.selection.select(边界, SelectionType.REPLACE, 0, true);
|
||||
新文档.paste();
|
||||
当前左侧 += 文件宽度数组[索引1];
|
||||
}
|
||||
|
||||
// 选择输出文件夹并保存图片
|
||||
var 输出文件夹 = new Folder(edittext1.text + "/拼贴");
|
||||
if (!输出文件夹.exists) 输出文件夹.create();
|
||||
|
||||
// 设置TIFF保存选项
|
||||
var tiffSaveOptions = new TiffSaveOptions();
|
||||
tiffSaveOptions.imageCompression = TIFFEncoding.TIFFLZW; // 使用LZW压缩
|
||||
tiffSaveOptions.byteOrder = ByteOrder.IBM; // 设置字节顺序为IBM(大端序)
|
||||
|
||||
// 定义保存的文件路径和名称
|
||||
var 文件 = new File(输出文件夹 + "/combined.tif");
|
||||
|
||||
// 保存当前文档为TIFF格式
|
||||
新文档.saveAs(文件, tiffSaveOptions, true, Extension.LOWERCASE);
|
||||
裁切透明像素();
|
||||
|
||||
// 关闭文档,不保存更改
|
||||
//新文档.close(SaveOptions.DONOTSAVECHANGES);
|
||||
dialog.close();
|
||||
alert("拼贴完成")
|
||||
|
||||
}
|
||||
|
||||
function 裁切透明像素() {
|
||||
var 文档 = app.activeDocument; // 获取当前活动的文档
|
||||
|
||||
// 裁切透明像素
|
||||
// TrimType.TOPLEFT 表示从图像的左上角开始裁切
|
||||
// 第二个参数 true 表示裁切顶部的透明像素
|
||||
// 第三个参数 true 表示裁切左侧的透明像素
|
||||
// 第四个参数 true 表示裁切底部的透明像素
|
||||
// 第五个参数 true 表示裁切右侧的透明像素
|
||||
文档.trim(TrimType.TOPLEFT, true, true, true, true);
|
||||
}
|
||||
|
||||
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
6
temp_backup/Server_redundant/tempdemo/psmark/JSX25.py
Normal file
@@ -0,0 +1,6 @@
|
||||
dxf25_jscode = """
|
||||
|
||||
alert("接口测试")
|
||||
|
||||
|
||||
"""
|
||||
218
temp_backup/Server_redundant/tempdemo/psmark/JSX26.py
Normal file
@@ -0,0 +1,218 @@
|
||||
dxf26_jscode = """
|
||||
// 弹出文件夹选择框
|
||||
|
||||
function 模特换图(){
|
||||
建立快照()
|
||||
var folder = Folder.selectDialog("请选择一个文件夹");
|
||||
var 模特文档 = app.activeDocument;
|
||||
|
||||
if (folder) {
|
||||
// 在选择的文件夹中创建一个新的子文件夹
|
||||
var targetFolder = new Folder(folder.fullName + "/模特图生成");
|
||||
if (!targetFolder.exists) {
|
||||
targetFolder.create();
|
||||
}
|
||||
|
||||
var files = folder.getFiles();
|
||||
|
||||
// 获取“贴图位置”图层组中的所有图层名称
|
||||
var layerSet = 模特文档.layerSets.getByName("贴图位置");
|
||||
var layerNames = [];
|
||||
for (var j = 0; j < layerSet.artLayers.length; j++) {
|
||||
layerNames.push(layerSet.artLayers[j].name);
|
||||
}
|
||||
|
||||
// 遍历并尝试打开每个文件
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
if (file instanceof File) {
|
||||
try {
|
||||
app.open(file);
|
||||
app.activeDocument.flatten()
|
||||
var 当前文档 = app.activeDocument;
|
||||
var 当前文档名称 = 当前文档.name;
|
||||
图像大小();
|
||||
预设图案(当前文档名称);
|
||||
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
app.activeDocument = 模特文档;
|
||||
var 水平增量 = 200;
|
||||
var 垂直增量 = 300;
|
||||
var 水平基数 = 200;
|
||||
var 垂直基数 = 300;
|
||||
// 遍历“贴图位置”图层组中的每个图层
|
||||
for (var k = 0; k < layerNames.length; k++) {
|
||||
var layname = layerNames[k];
|
||||
var 贴图位置 = app.activeDocument.layerSets.getByName("贴图位置").layers.getByName(layname);
|
||||
app.activeDocument.activeLayer = 贴图位置;
|
||||
载入选区();
|
||||
填充图案(当前文档名称);
|
||||
栅格化图层()
|
||||
取消链接蒙版()
|
||||
var 水平 = 水平基数 + 水平增量 * k;
|
||||
var 垂直 = 垂直基数 + 垂直增量 * k;
|
||||
位移(水平, 垂直);
|
||||
}
|
||||
|
||||
var saveFile = new File(targetFolder.fullName + "/" + file.name.replace(/\.[^\.]+$/, "") + ".tif");
|
||||
var tiffSaveOptions = new TiffSaveOptions();
|
||||
tiffSaveOptions.imageCompression = TIFFEncoding.NONE; // 或者根据需要设置其他压缩选项
|
||||
tiffSaveOptions.alphaChannels = true;
|
||||
tiffSaveOptions.layers = true;
|
||||
app.activeDocument.saveAs(saveFile, tiffSaveOptions, true, Extension.LOWERCASE);
|
||||
|
||||
// 关闭当前文档
|
||||
历史记录回退到快照1()
|
||||
} catch (e) {
|
||||
alert("无法打开文件: " );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
alert("换图完成")
|
||||
} else {
|
||||
alert("没有选择文件夹");
|
||||
}
|
||||
|
||||
// 其他函数保持不变
|
||||
|
||||
|
||||
function generateRandomNumber(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 图像大小() //图像大小
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), 150);
|
||||
d.putBoolean(stringIDToTypeID("scaleStyles"), true);
|
||||
d.putBoolean(stringIDToTypeID("constrainProportions"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("imageSize"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 取消链接蒙版() //取消链接蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putBoolean(stringIDToTypeID("userMaskLinked"), false);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layer"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 栅格化图层() //栅格化图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("rasterizeLayer"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 预设图案(当前文档名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("pattern"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("using"), r1);
|
||||
d.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 填充图案(当前文档名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("contentLayer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
var d2 = new ActionDescriptor();
|
||||
var d3 = new ActionDescriptor();
|
||||
d3.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||
|
||||
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 位移(水平,垂直) //位移
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putInteger(stringIDToTypeID("horizontal"), 水平);
|
||||
d.putInteger(stringIDToTypeID("vertical"), 垂直);
|
||||
d.putEnumerated(stringIDToTypeID("fill"), stringIDToTypeID("fillMode"), stringIDToTypeID("wrap"));
|
||||
executeAction(stringIDToTypeID("offset"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 历史记录回退到快照1() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("snapshotClass"), "快照 1");
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 建立快照() //打开
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("snapshotClass"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putProperty(stringIDToTypeID("historyState"), stringIDToTypeID("currentHistoryState"));
|
||||
d.putReference(stringIDToTypeID("from"), r1);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
}
|
||||
"""
|
||||
500
temp_backup/Server_redundant/tempdemo/psmark/JSX27.py
Normal file
@@ -0,0 +1,500 @@
|
||||
dxf27_jscode = """
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 新的米样缩放()
|
||||
{
|
||||
var dialog = new Window("dialog");
|
||||
|
||||
dialog.text = "S/O样自动缩放";
|
||||
//dialog.text = "SO自动米样拼贴";
|
||||
dialog.orientation = "row";
|
||||
dialog.alignChildren = ["left","top"];
|
||||
dialog.spacing = 10;
|
||||
dialog.margins = 16;
|
||||
|
||||
// GROUP1
|
||||
// ======
|
||||
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||
group1.orientation = "column";
|
||||
group1.alignChildren = ["fill","top"];
|
||||
group1.spacing = 10;
|
||||
group1.margins = 0;
|
||||
|
||||
// PANEL1
|
||||
// ======
|
||||
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||
panel1.text = "源图像";
|
||||
panel1.preferredSize.width = 388;
|
||||
panel1.preferredSize.height = 205;
|
||||
panel1.orientation = "column";
|
||||
panel1.alignChildren = ["left","top"];
|
||||
panel1.spacing = 10;
|
||||
panel1.margins = 10;
|
||||
|
||||
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||
statictext1.text = "使用:";
|
||||
|
||||
// GROUP2
|
||||
// ======
|
||||
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||
group2.orientation = "row";
|
||||
group2.alignChildren = ["left","center"];
|
||||
group2.spacing = 10;
|
||||
group2.margins = 0;
|
||||
|
||||
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||
button1.text = "选取";
|
||||
|
||||
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||
edittext1.text = "[选择图像文件夹]";
|
||||
edittext1.preferredSize.width = 300;
|
||||
|
||||
// PANEL2
|
||||
// ======
|
||||
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||
panel2.text = "SO小样文档";
|
||||
panel2.preferredSize.height = 160;
|
||||
panel2.orientation = "column";
|
||||
panel2.alignChildren = ["left","top"];
|
||||
panel2.spacing = 10;
|
||||
panel2.margins = 10;
|
||||
|
||||
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||
statictext2.text = "设定:";
|
||||
|
||||
// GROUP3
|
||||
// ======
|
||||
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||
group3.orientation = "row";
|
||||
group3.alignChildren = ["left","center"];
|
||||
group3.spacing = 10;
|
||||
group3.margins = 0;
|
||||
|
||||
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||
statictext3.text = "宽度(cm):";
|
||||
|
||||
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||
edittext2.preferredSize.width = 100;
|
||||
|
||||
var statictext4 = group3.add("statictext", undefined, undefined, {name: "statictext4"});
|
||||
statictext4.text = "高度(cm):";
|
||||
|
||||
var edittext3 = group3.add('edittext {properties: {name: "edittext3"}}');
|
||||
edittext3.preferredSize.width = 100;
|
||||
|
||||
// GROUP4
|
||||
// ======
|
||||
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||
group4.orientation = "row";
|
||||
group4.alignChildren = ["left","center"];
|
||||
group4.spacing = 10;
|
||||
group4.margins = 0;
|
||||
|
||||
// GROUP5
|
||||
// ======
|
||||
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||
group5.orientation = "row";
|
||||
group5.alignChildren = ["left","center"];
|
||||
group5.spacing = 10;
|
||||
group5.margins = 0;
|
||||
|
||||
// PANEL3
|
||||
// ======
|
||||
|
||||
|
||||
// GROUP7
|
||||
// ======
|
||||
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||
group7.orientation = "column";
|
||||
group7.alignChildren = ["fill","top"];
|
||||
group7.spacing = 10;
|
||||
group7.margins = 0;
|
||||
|
||||
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||
ok.text = "确认";
|
||||
|
||||
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||
cancel.text = "取消";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
button1.onClick = function () {
|
||||
// 打开文件夹选择对话框
|
||||
var selectedFolder = Folder.selectDialog("选择图像文件夹");
|
||||
|
||||
// 检查用户是否取消了选择
|
||||
if (selectedFolder) {
|
||||
// 将选择的文件夹路径显示在输入框中
|
||||
edittext1.text = selectedFolder.fsName;
|
||||
}
|
||||
};
|
||||
|
||||
ok.onClick = function () {
|
||||
app.preferences.rulerUnits = Units.PIXELS;
|
||||
var 素材图片文件夹 = new Folder(edittext1.text);
|
||||
var 遍历tiff = 素材图片文件夹.getFiles("*.*");
|
||||
|
||||
// 新建文件夹
|
||||
var 新文件夹 = new Folder(edittext1.text + "/SO小样拼贴");
|
||||
新文件夹.create();
|
||||
var 新加字文件夹 = new Folder(edittext1.text + "/SO小样拼贴加字");
|
||||
新加字文件夹.create();
|
||||
|
||||
|
||||
宽度=Number (edittext2.text);
|
||||
高度=Number (edittext3.text);
|
||||
|
||||
|
||||
for (var i = 0; i < 遍历tiff.length; i++) {
|
||||
if (遍历tiff[i] instanceof File) {
|
||||
app.open(遍历tiff[i]);
|
||||
app.activeDocument.flatten();
|
||||
var 当前文档 = app.activeDocument;
|
||||
var 当前文档名称 = 当前文档.name;
|
||||
resizeImageToCm(宽度, 高度,200);
|
||||
/*
|
||||
图像大小()
|
||||
预设图案(当前文档名称);
|
||||
app.activeDocument.crop([UnitValue("0px"), UnitValue("0px"), UnitValue(宽度, "cm"), UnitValue(高度, "cm")]);
|
||||
填充图案(当前文档名称);
|
||||
app.activeDocument.flatten();
|
||||
画布扩展()
|
||||
*/
|
||||
画布扩展()
|
||||
// 保存新的图像文件到新建的文件夹中
|
||||
var 保存路径 = 新文件夹 + "/" + 当前文档名称;
|
||||
var tifSaveOpt = new TiffSaveOptions();
|
||||
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||
var asCopy = true;
|
||||
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||
创建并处理文本图层();
|
||||
app.activeDocument.flatten();
|
||||
var 保存路径 = 新加字文件夹 + "/" + 当前文档名称;
|
||||
var tifSaveOpt = new TiffSaveOptions();
|
||||
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||
var asCopy = true;
|
||||
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
//alert("小样连晒完成")
|
||||
dialog.close();
|
||||
// 创建并保存拼贴图像(新文件夹, 幅宽)
|
||||
};
|
||||
|
||||
|
||||
function resizeImageToCm(widthCm, heightCm, resolution) {
|
||||
// 将厘米转换为像素
|
||||
var widthInPixels = widthCm * (resolution / 2.54);
|
||||
var heightInPixels = heightCm * (resolution / 2.54);
|
||||
|
||||
// 调整图像大小
|
||||
app.activeDocument.resizeImage(widthInPixels, heightInPixels, resolution, ResampleMethod.NEARESTNEIGHBOR);
|
||||
}
|
||||
|
||||
// 使用示例:将图像大小调整为 10cm x 15cm,分辨率为 300 像素/英寸
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 预设图案(当前文档名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("pattern"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("using"), r1);
|
||||
d.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 填充图案(当前文档名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("contentLayer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
var d2 = new ActionDescriptor();
|
||||
var d3 = new ActionDescriptor();
|
||||
d3.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||
|
||||
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 图像大小() //图像大小
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), 200);
|
||||
d.putBoolean(stringIDToTypeID("scaleStyles"), true);
|
||||
d.putBoolean(stringIDToTypeID("constrainProportions"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("imageSize"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 画布扩展() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||
d.putEnumerated(stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("color"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putDouble(stringIDToTypeID("red"), 255);
|
||||
d1.putDouble(stringIDToTypeID("green"), 255);
|
||||
d1.putDouble(stringIDToTypeID("blue"), 255);
|
||||
d.putObject(stringIDToTypeID("canvasExtensionColor"), stringIDToTypeID("RGBColor"), d1);
|
||||
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 创建并处理文本图层() {
|
||||
// 新建图层
|
||||
var textLayer = activeDocument.artLayers.add();
|
||||
|
||||
// 将新建图层变成文本图层
|
||||
textLayer.kind = LayerKind.TEXT;
|
||||
|
||||
// 将文本内容改为当前文档name
|
||||
textLayer.textItem.contents = activeDocument.name;
|
||||
|
||||
// 字体大小固定值
|
||||
var 固定字体大小 = 30; // 例如,30像素
|
||||
textLayer.textItem.size = 固定字体大小;
|
||||
|
||||
// 文字字体固定值
|
||||
textLayer.textItem.font = "微软雅黑";
|
||||
|
||||
// 计算并调整文本位置
|
||||
var x = activeDocument.width - textLayer.bounds[2];
|
||||
var y = textLayer.bounds[1];
|
||||
textLayer.translate(x, -y);
|
||||
|
||||
// 偏移
|
||||
textLayer.translate(UnitValue("-1cm"), UnitValue("+0.5cm"));
|
||||
|
||||
// 复制并栅格化图层
|
||||
var copyLayer = textLayer.duplicate();
|
||||
copyLayer.rasterize(RasterizeType.ENTIRELAYER);
|
||||
|
||||
// 设置固定颜色值
|
||||
var 固定颜色 = new SolidColor();
|
||||
固定颜色.rgb.red = 255; // 红色分量
|
||||
固定颜色.rgb.green = 255; // 绿色分量
|
||||
固定颜色.rgb.blue = 255; // 蓝色分量
|
||||
|
||||
activeDocument.activeLayer = copyLayer;
|
||||
activeDocument.selection.fill(固定颜色, ColorBlendMode.NORMAL, 100, true);
|
||||
|
||||
// 白边
|
||||
activeDocument.activeLayer.applyMinimum(10);
|
||||
后移一层()
|
||||
向上选择()
|
||||
向下合并()
|
||||
名称更改字体()
|
||||
缩小字体图层至文档一半()
|
||||
多选背景()
|
||||
底对齐()
|
||||
水平居中对齐()
|
||||
}
|
||||
|
||||
// 调用函数
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 后移一层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 向上选择() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(19);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
function 向下合并() //向下合并
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 名称更改字体() //名称更改
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putString(stringIDToTypeID("name"), "字体");
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layer"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 缩小字体图层至文档一半() {
|
||||
var 文档 = app.activeDocument;
|
||||
var 字体图层 = null;
|
||||
|
||||
// 遍历文档中的图层以找到名为"字体"的图层
|
||||
for (var i = 0; i < 文档.artLayers.length; i++) {
|
||||
if (文档.artLayers[i].name === "字体") {
|
||||
字体图层 = 文档.artLayers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (字体图层 !== null) {
|
||||
// 获取文档的宽度的一半
|
||||
var 目标宽度 = 文档.width / 2;
|
||||
|
||||
// 获取图层的当前宽度
|
||||
var 图层宽度 = 字体图层.bounds[2] - 字体图层.bounds[0];
|
||||
|
||||
// 计算缩放比例
|
||||
var 缩放比例 = 目标宽度 / 图层宽度 * 100;
|
||||
|
||||
// 缩放图层
|
||||
字体图层.resize(缩放比例, 缩放比例, AnchorPosition.MIDDLECENTER);
|
||||
} else {
|
||||
alert("未找到名为'字体'的图层");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function 多选背景() //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), "背景");
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(1);
|
||||
list.putInteger(13);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 底对齐() //底对齐
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSBottoms"));
|
||||
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 水平居中对齐() //水平居中对齐
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSCentersH"));
|
||||
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 设置单位为像素
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
2203
temp_backup/Server_redundant/tempdemo/psmark/JSX3.py
Normal file
1909
temp_backup/Server_redundant/tempdemo/psmark/JSX4.py
Normal file
2468
temp_backup/Server_redundant/tempdemo/psmark/JSX5.py
Normal file
778
temp_backup/Server_redundant/tempdemo/psmark/JSX6.py
Normal file
@@ -0,0 +1,778 @@
|
||||
dxf6_jscode = """
|
||||
|
||||
|
||||
function 前景色修改() //取消选择
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("foregroundColor"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putDouble(stringIDToTypeID("cyan"), 20);
|
||||
d1.putDouble(stringIDToTypeID("magenta"), 0);
|
||||
d1.putDouble(stringIDToTypeID("yellowColor"), 0);
|
||||
d1.putDouble(stringIDToTypeID("black"), 0);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("CMYKColorClass"), d1);
|
||||
d.putString(stringIDToTypeID("source"), "photoshopPicker");
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 图像切割2() {
|
||||
|
||||
app.activeDocument.suspendHistory("图像切割", "图像切割()");
|
||||
}
|
||||
|
||||
|
||||
function 图像切割() {
|
||||
画布大小()
|
||||
app.preferences.rulerUnits = Units.PIXELS
|
||||
var currentDocument = app.activeDocument;
|
||||
var matchCount = 0; // 匹配到的数值计数
|
||||
var existingPatternSet = false;
|
||||
var layerNames = []; // 保存匹配到的图层名称的数组
|
||||
|
||||
// 遍历图层
|
||||
for (var j = 0; j < currentDocument.layers.length; j++) {
|
||||
var layer = currentDocument.layers[j];
|
||||
var layerName = layer.name;
|
||||
|
||||
// 检查图层名称是否以P开头并且后面跟着数字
|
||||
if (/^P\d+$/.test(layerName)) {
|
||||
matchCount++;
|
||||
layerNames.push(layer); // 将匹配到的图层添加到数组中
|
||||
}
|
||||
}
|
||||
|
||||
// 输出匹配到的数值个数
|
||||
// $.writeln("匹配到的数值个数:" + matchCount);
|
||||
|
||||
// 遍历匹配到的图层名称
|
||||
for (var i = 0; i < layerNames.length; i++) {
|
||||
var layerName = layerNames[i].name;
|
||||
$.writeln("匹配到的图层名称:" + layerName);
|
||||
var 当前花样图层 = app.activeDocument.layers.getByName(layerName);
|
||||
app.activeDocument.activeLayer = 当前花样图层;
|
||||
切换mask()
|
||||
载入选区()
|
||||
var 边距 = 获取当前选区四边距();
|
||||
var 毫米 = 130;
|
||||
var 每英寸像素数 = app.activeDocument.resolution; // 获取当前文档的分辨率(每英寸像素数)
|
||||
var 扩展像素 = 毫米转像素(毫米, 每英寸像素数);
|
||||
|
||||
var 裁切上边距= 边距.top-扩展像素
|
||||
var 裁切左边距= 边距.left-扩展像素
|
||||
var 裁切下边距= 边距.bottom+扩展像素
|
||||
var 裁切右边距= 边距.right+扩展像素
|
||||
|
||||
var selRegion = [
|
||||
[裁切左边距,裁切上边距],
|
||||
[裁切右边距,裁切上边距],
|
||||
[裁切右边距,裁切下边距],
|
||||
[裁切左边距,裁切下边距]
|
||||
];
|
||||
|
||||
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||
新建图层()
|
||||
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||
|
||||
var c = new SolidColor();
|
||||
c.rgb.hexValue = "FFFFFF";
|
||||
app.activeDocument.selection.fill(c);
|
||||
后移一层()
|
||||
app.activeDocument.activeLayer = 当前花样图层;
|
||||
切换mask()
|
||||
载入选区()
|
||||
删除图层蒙版()
|
||||
创建剪贴蒙版()
|
||||
向下合并()
|
||||
添加图层蒙版()
|
||||
当前图层=app.activeDocument.activeLayer
|
||||
当前图层.name=layerName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 切换mask() //取消选择
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 毫米转像素(毫米, 每英寸像素数) {
|
||||
var 每英寸毫米数 = 25.4;
|
||||
var 英寸 = 毫米 / 每英寸毫米数;
|
||||
return Math.round(英寸 * 每英寸像素数);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 画布大小() //画布大小
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 850.56);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 850.56);
|
||||
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 获取当前选区四边距() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var selectionBounds = currentDocument.selection.bounds;
|
||||
|
||||
var top = selectionBounds[1].value;
|
||||
var left = selectionBounds[0].value;
|
||||
var bottom = selectionBounds[3].value;
|
||||
var right = selectionBounds[2].value;
|
||||
|
||||
return {
|
||||
top: top,
|
||||
left: left,
|
||||
bottom: bottom,
|
||||
right: right
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 新建图层() //新建图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("layer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putInteger(stringIDToTypeID("layerID"), 135);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 后移一层() //后移一层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 删除图层蒙版() //删除图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 删除图层蒙版() //删除图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 向下合并() //向下合并
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 添加图层蒙版() //添加图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("at"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 创建剪贴蒙版() //创建剪贴蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("groupEvent"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 右下对齐2() {
|
||||
|
||||
app.activeDocument.suspendHistory("右下对齐", "右下对齐()");
|
||||
}
|
||||
|
||||
|
||||
function 右下对齐() {
|
||||
var groupName = 获取当前图层组名称();
|
||||
if (groupName !== null) {
|
||||
app.preferences.rulerUnits = Units.PIXELS;
|
||||
var splitGroupName = groupName.split("-");
|
||||
var firstPart = splitGroupName[0];
|
||||
} else {
|
||||
// 如果未获取到当前图层组名称,退出程序
|
||||
// alert("未获取到当前图层组名称!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
当前花样图层 = app.activeDocument.layers.getByName(firstPart);
|
||||
} catch (e) {
|
||||
// 处理异常情况
|
||||
alert("没有找到对应的花样裁片: ");
|
||||
return; // 中断函数执行
|
||||
}
|
||||
|
||||
app.activeDocument.activeLayer = 当前花样图层;
|
||||
|
||||
切换mask();
|
||||
载入选区蒙版()
|
||||
// 应用图层蒙版();
|
||||
// 载入选区();
|
||||
|
||||
var 边距 = 获取当前选区四边距();
|
||||
var 获取左右的中心坐标 = (边距.right - 边距.left) / 2 + 边距.left;
|
||||
var 获取右上的坐标 = 边距.right
|
||||
$.writeln("中心坐标=:" + 获取左右的中心坐标);
|
||||
|
||||
var currentDocument = app.activeDocument;
|
||||
var height = currentDocument.height.value;
|
||||
|
||||
var 上边距新 = 0;
|
||||
var 左边距新 = 获取右上的坐标-1 ;
|
||||
var 下边距新 = height;
|
||||
var 右边距新 = 获取右上的坐标 + 1;
|
||||
|
||||
var 边距 = 获取当前选区四边距();
|
||||
$.writeln("上边距:" + 边距.top);
|
||||
$.writeln("左边距:" + 边距.left);
|
||||
$.writeln("下边距:" + 边距.bottom);
|
||||
$.writeln("右边距:" + 边距.right);
|
||||
|
||||
//历史记录回退领口函数();
|
||||
新建选区(上边距新, 左边距新, 下边距新, 右边距新);
|
||||
app.activeDocument.activeLayer = 当前花样图层;
|
||||
切换mask();
|
||||
选区减去();
|
||||
|
||||
获取右上的坐标 = 获取当前选区四边距();
|
||||
获取右上的坐标y坐标信息 = 获取右上的坐标.bottom; //////////////////////以上的是获取花样的的中心坐标信息
|
||||
|
||||
获取右上的坐标x坐标信息 = 获取右上的坐标.left;
|
||||
// $.writeln("居中领口y坐标信息" + 获取到花样图层当前居中领口y坐标信息);
|
||||
//$.writeln("居中领口x坐标信息" + 获取到花样图层当前居中领口x坐标信息);
|
||||
|
||||
//////////////////////以上的是获取花样的的中心坐标信息
|
||||
|
||||
|
||||
var currentDocument = app.activeDocument;
|
||||
var targetLayerSet = currentDocument.layerSets.getByName(groupName);
|
||||
|
||||
if (targetLayerSet) {
|
||||
var layers = targetLayerSet.layers;
|
||||
|
||||
if (layers.length > 0) {
|
||||
for (var i = 0; i < layers.length; i++) {
|
||||
var layer = layers[i];
|
||||
var 裁片图层 = layer.name;
|
||||
$.writeln("裁片图层坐标信息=" + 裁片图层);
|
||||
|
||||
try {
|
||||
var 当前裁片图层 = app.activeDocument.layerSets.getByName(groupName).layers.getByName(裁片图层);
|
||||
} catch (e) {
|
||||
// 处理异常情况
|
||||
alert("没有找到对应的花样裁片 " );
|
||||
return; // 中断函数执行
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
app.activeDocument.activeLayer = 当前裁片图层;
|
||||
载入选区11()
|
||||
//var selectedLayer = app.activeDocument.activeLayer;
|
||||
// var bounds = selectedLayer.bounds;
|
||||
/*
|
||||
图层上边距新 = 0;
|
||||
图层左边距新 = 右right ;
|
||||
图层下边距新 = height;
|
||||
图层右边距新 = 右right +1;
|
||||
|
||||
|
||||
左left = bounds[0].value;
|
||||
上top = bounds[1].value;
|
||||
右right = bounds[2].value;
|
||||
下bottom = bounds[3].value;
|
||||
中心坐标centerX = (bounds[2].value - bounds[0].value) / 2 + bounds[0].value;
|
||||
|
||||
中心坐标centerY = (bounds[3].value - bounds[1].value) / 2 + bounds[1].value;
|
||||
*/
|
||||
// $.writeln("中心坐标centerX" + 中心坐标centerX);
|
||||
// $.writeln("中心坐标centerY" +中心坐标cent*erY);
|
||||
|
||||
获取花样右上的坐标裁片位置坐标 = 获取当前选区四边距();
|
||||
|
||||
图层上边距新 = 0;
|
||||
图层左边距新 = 获取花样右上的坐标裁片位置坐标.right-1 ;
|
||||
图层下边距新 = height;
|
||||
图层右边距新 = 获取花样右上的坐标裁片位置坐标.right +1;
|
||||
|
||||
|
||||
新建选区(图层上边距新, 图层左边距新, 图层下边距新, 图层右边距新);
|
||||
app.activeDocument.activeLayer = 当前裁片图层;
|
||||
载入选区交叉图层()
|
||||
获取右上的坐标裁片位置坐标 = 获取当前选区四边距();
|
||||
获取右上的坐标裁片位置坐标x=获取右上的坐标裁片位置坐标.left
|
||||
获取右上的坐标裁片位置坐标y=获取右上的坐标裁片位置坐标.bottom
|
||||
|
||||
位移距离PXy = 获取右上的坐标y坐标信息 - 获取右上的坐标裁片位置坐标y;
|
||||
位移距离PXx = 获取右上的坐标x坐标信息 - 获取右上的坐标裁片位置坐标x;
|
||||
$.writeln(位移距离PXy);
|
||||
$.writeln(位移距离PXx);
|
||||
取消选择()
|
||||
自由变换2(位移距离PXy,位移距离PXx)
|
||||
//alert("移动")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
取消选择();
|
||||
}
|
||||
|
||||
function 获取当前图层组名称() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var currentLayer = currentDocument.activeLayer;
|
||||
|
||||
if (currentLayer.typename === "LayerSet") {
|
||||
var groupName = currentLayer.name;
|
||||
return groupName;
|
||||
} else {
|
||||
alert("当前图层不是图层组。");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 载入选区交叉图层() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("with"), r1);
|
||||
executeAction(charIDToTypeID("Intr"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
function 载入选区蒙版() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 遍历图层组内的图层(图层组名称) {
|
||||
var currentDocument = app.activeDocument;
|
||||
var targetLayerSet = currentDocument.layerSets.getByName(图层组名称);
|
||||
|
||||
if (targetLayerSet) {
|
||||
var layers = targetLayerSet.layers;
|
||||
|
||||
if (layers.length > 0) {
|
||||
for (var i = 0; i < layers.length; i++) {
|
||||
var layer = layers[i];
|
||||
// 在这里对每个图层进行进一步的操作
|
||||
$.writeln("图层名称:" + layer.name);
|
||||
}
|
||||
} else {
|
||||
$.writeln("图层组中没有任何图层。");
|
||||
}
|
||||
} else {
|
||||
$.writeln("找不到指定名称的图层组。");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 新建选区(上边距, 左边距, 下边距, 右边距) {
|
||||
var currentDocument = app.activeDocument;
|
||||
var top = 上边距;
|
||||
var left = 左边距;
|
||||
var bottom = 下边距;
|
||||
var right = 右边距;
|
||||
|
||||
var selectionRegion = Array(Array(left, top), Array(right, top), Array(right, bottom), Array(left, bottom));
|
||||
currentDocument.selection.select(selectionRegion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 选区减去() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("with"), r1);
|
||||
executeAction(charIDToTypeID("Intr"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 将像素转换为毫米
|
||||
function pixelsToMillimeters(pixels) {
|
||||
// 获取当前文档
|
||||
var doc = app.activeDocument;
|
||||
|
||||
// 获取图像的分辨率(像素/英寸)
|
||||
var resolution = doc.resolution;
|
||||
|
||||
// 计算像素转换为毫米
|
||||
var inches = pixels / resolution;
|
||||
var millimeters = inches * 25.4;
|
||||
|
||||
return millimeters.toFixed(2); // 保留两位小数
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 切换mask() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 获取当前选区四边距() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var selectionBounds = currentDocument.selection.bounds;
|
||||
|
||||
var top = selectionBounds[1].value;
|
||||
var left = selectionBounds[0].value;
|
||||
var bottom = selectionBounds[3].value;
|
||||
var right = selectionBounds[2].value;
|
||||
|
||||
return {
|
||||
top: top,
|
||||
left: left,
|
||||
bottom: bottom,
|
||||
right: right
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function 获取当前文档四边距() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var documentBounds = currentDocument.bounds;
|
||||
|
||||
var top = documentBounds[1].value;
|
||||
var left = documentBounds[0].value;
|
||||
var bottom = documentBounds[3].value;
|
||||
var right = documentBounds[2].value;
|
||||
|
||||
return {
|
||||
top: top,
|
||||
left: left,
|
||||
bottom: bottom,
|
||||
right: right
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function 选区减去2() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("with"), r1);
|
||||
executeAction(charIDToTypeID("Intr"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 遍历图层组内的图层(图层组名称) {
|
||||
var currentDocument = app.activeDocument;
|
||||
var targetLayerSet = currentDocument.layerSets.getByName(图层组名称);
|
||||
|
||||
if (targetLayerSet) {
|
||||
var layers = targetLayerSet.layers;
|
||||
|
||||
if (layers.length > 0) {
|
||||
for (var i = 0; i < layers.length; i++) {
|
||||
var layer = layers[i];
|
||||
// 在这里对每个图层进行进一步的操作
|
||||
$.writeln("图层名称:" + layer.name);
|
||||
}
|
||||
} else {
|
||||
$.writeln("图层组中没有任何图层。");
|
||||
}
|
||||
} else {
|
||||
$.writeln("找不到指定名称的图层组。");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function 自由变换2(位移距离PXy,位移距离PXx) //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 位移距离PXx);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 位移距离PXy);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d1);
|
||||
d.putBoolean(stringIDToTypeID("linked"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function 历史记录回退领口函数() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putOffset(stringIDToTypeID("historyState"), -2 );
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
function 取消选择() //取消选择
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("to"), stringIDToTypeID("ordinal"), stringIDToTypeID("none"));
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 历史记录回退1领口函数() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putOffset(stringIDToTypeID("historyState"), -1 );
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function 载入选区11() //载入选区
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
function 取消选择() //取消选择
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("to"), stringIDToTypeID("ordinal"), stringIDToTypeID("none"));
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1628
temp_backup/Server_redundant/tempdemo/psmark/JSX7.py
Normal file
1742
temp_backup/Server_redundant/tempdemo/psmark/JSX8.py
Normal file
983
temp_backup/Server_redundant/tempdemo/psmark/JSX9.py
Normal file
@@ -0,0 +1,983 @@
|
||||
dxf9_jscode = """
|
||||
|
||||
|
||||
function 裁片射出比例缩放按中心点() {
|
||||
app.preferences.rulerUnits = Units.PIXELS
|
||||
var 主文档 = app.activeDocument;
|
||||
var 主文档名称 = 主文档.name;
|
||||
|
||||
// 遍历当前打开的文档
|
||||
for (var i = 0; i < app.documents.length; i++) {
|
||||
var document = app.documents[i];
|
||||
var documentName = document.name;
|
||||
|
||||
// 判断文档名称是否与主文档名称不相同
|
||||
if (documentName !== 主文档名称) {
|
||||
app.activeDocument = document;
|
||||
遍历图层();
|
||||
}
|
||||
}
|
||||
|
||||
function 遍历图层() {
|
||||
var layerNames = []; // 用于存储图层名称的数组
|
||||
var currentDocument = app.activeDocument;
|
||||
|
||||
for (var j = 0; j < currentDocument.layers.length; j++) {
|
||||
var layer = currentDocument.layers[j];
|
||||
var layerName = layer.name;
|
||||
layerNames.push(layerName);
|
||||
}
|
||||
|
||||
// 逐个处理图层
|
||||
for (var k = 0; k < layerNames.length; k++) {
|
||||
var 当前图层名称 = layerNames[k];
|
||||
// $.writeln("图层名称:" + 当前图层名称);
|
||||
// alert(当前图层名称);
|
||||
|
||||
var parts = 当前图层名称.split("-");
|
||||
if (parts.length > 0) {
|
||||
var 裁片名称 = parts[0];
|
||||
app.activeDocument = 主文档;
|
||||
$.writeln(裁片名称);
|
||||
初始化模板裁片名称 = 当前图层名称.split("-");
|
||||
初始化码数裁片名称 = 当前图层名称.split("_");
|
||||
大货组名称 =初始化模板裁片名称[0]+("-大货裁片")
|
||||
实际裁片名称 = 初始化模板裁片名称[0]+"-"+初始化码数裁片名称[2]
|
||||
$.writeln(大货组名称);
|
||||
$.writeln(实际裁片名称);
|
||||
var 空白裁片模板 = app.activeDocument.layerSets.getByName(大货组名称).layers.getByName(实际裁片名称 );
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
载入选区()
|
||||
|
||||
var 边距 = 获取当前选区四边距();
|
||||
var 当前选区高度=边距.bottom-边距.top
|
||||
var 当前选区宽度=边距.right-边距.left
|
||||
var 高度转毫米 = pixelsToMillimeters(当前选区高度);
|
||||
var 宽度转毫米 = pixelsToMillimeters(当前选区宽度);
|
||||
|
||||
var 搜索词 = 裁片名称;
|
||||
var 匹配图层数组 = 匹配图层名(搜索词);
|
||||
|
||||
// 显示匹配的图层列表
|
||||
if (匹配图层数组.length > 0) {
|
||||
var 图层列表文本 = "匹配的图层列表:";
|
||||
for (var i = 0; i < 匹配图层数组.length; i++) {
|
||||
if (i !== 0) {
|
||||
图层列表文本 += " ";
|
||||
}
|
||||
图层列表文本 += 匹配图层数组[i].name;
|
||||
}
|
||||
var 数据解析分割=图层列表文本.split("_");
|
||||
//var 实际套花名称=名称部分[0]
|
||||
var 基码图层宽度 = parseFloat(数据解析分割[1]);
|
||||
var 基码图层高度 = parseFloat(数据解析分割[2]);
|
||||
var 缩放比例高度=高度转毫米/基码图层高度*100
|
||||
var 缩放比例宽度=宽度转毫米/基码图层宽度*100
|
||||
// alert(基码图层宽度);
|
||||
} else {
|
||||
alert("没有找到匹配的图层。");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
$.writeln("上边距:" + 边距.top);
|
||||
$.writeln("左边距:" + 边距.left);
|
||||
$.writeln("下边距:" + 边距.bottom);
|
||||
$.writeln("右边距:" + 边距.right);
|
||||
*/8
|
||||
// 示例用法:
|
||||
var 毫米 = 300;
|
||||
var 每英寸像素数 = app.activeDocument.resolution; // 获取当前文档的分辨率(每英寸像素数)
|
||||
var 扩展像素 = 毫米转像素(毫米, 每英寸像素数);
|
||||
|
||||
var 裁切上边距= 边距.top-扩展像素
|
||||
var 裁切左边距= 边距.left-扩展像素
|
||||
var 裁切下边距= 边距.bottom+扩展像素
|
||||
var 裁切右边距= 边距.right+扩展像素
|
||||
$.writeln(裁切上边距);
|
||||
$.writeln(裁切左边距);
|
||||
$.writeln(裁切下边距);
|
||||
$.writeln(裁切右边距);
|
||||
裁切图层(裁切上边距,裁切左边距,裁切下边距,裁切右边距)
|
||||
|
||||
var 空白裁片模板 = app.activeDocument.layerSets.getByName(大货组名称).layers.getByName(实际裁片名称 );
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
载入选区()
|
||||
var 缩放定位点的中心坐标=获取当前缩放定位点选区四边距()
|
||||
var 缩放定位点的Y轴坐标=缩放定位点的中心坐标.top2+(缩放定位点的中心坐标.bottom2-缩放定位点的中心坐标.top2)/2
|
||||
var 缩放定位点的X轴坐标=缩放定位点的中心坐标.left2+(缩放定位点的中心坐标.right2-缩放定位点的中心坐标.left2)/2
|
||||
$.writeln("Y轴中心坐标"+缩放定位点的Y轴坐标);
|
||||
$.writeln("X轴中心坐标"+缩放定位点的X轴坐标);
|
||||
|
||||
var 裁片 = app.activeDocument.layers.getByName(裁片名称);
|
||||
app.activeDocument.activeLayer = 裁片
|
||||
//var 空白裁片模板 = app.activeDocument.layerSets.getByName(大货组名称).layers.getByName(实际裁片名称 );
|
||||
//app.activeDocument.activeLayer = 空白裁片模板;
|
||||
取消选择()
|
||||
图层按照缩放定位点进行宽高缩放(缩放定位点的X轴坐标,缩放定位点的Y轴坐标, 缩放比例高度)
|
||||
|
||||
// var 裁片 = app.activeDocument.layers.getByName(裁片名称);
|
||||
// app.activeDocument.activeLayer = 裁片;
|
||||
|
||||
var 空白裁片模板 = app.activeDocument.layerSets.getByName(大货组名称).layers.getByName(实际裁片名称 );
|
||||
app.activeDocument.activeLayer = 空白裁片模板;
|
||||
载入选区()
|
||||
var 裁片 = app.activeDocument.layers.getByName(裁片名称);
|
||||
app.activeDocument.activeLayer = 裁片
|
||||
添加图层蒙版()
|
||||
应用图层蒙版()
|
||||
裁片.copy();
|
||||
历史记录回退()
|
||||
app.activeDocument = currentDocument;
|
||||
图层选择(当前图层名称);
|
||||
载入选区();
|
||||
粘贴图层();
|
||||
|
||||
取消选择();
|
||||
// app.refresh();
|
||||
|
||||
|
||||
var 裁片名称 = 当前图层名称.split("_");
|
||||
if (裁片名称.length > 1) {
|
||||
var 角度信息 = 裁片名称[1];
|
||||
|
||||
if (角度信息 === "180" || 角度信息 === "-180") {
|
||||
自由变换();
|
||||
} else if (角度信息 === "-90") {
|
||||
逆时针90旋转()
|
||||
|
||||
} else if (角度信息 === "90") {
|
||||
|
||||
顺时针90旋转()
|
||||
} else {
|
||||
// 如果以上条件都不满足,则执行默认的代码
|
||||
}
|
||||
|
||||
|
||||
|
||||
//历史记录回退缩放函数()
|
||||
}
|
||||
app.activeDocument = 主文档;
|
||||
历史记录回退缩放函数()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
app.activeDocument = currentDocument;
|
||||
烧花线添加()//alert("当前码拍好")///////////////////////////////////这里可以填写添加烧花线函数
|
||||
|
||||
}
|
||||
//alert("排版完成,请检查文件!!!")
|
||||
app.activeDocument = 主文档;
|
||||
}
|
||||
|
||||
|
||||
// 将像素转换为毫米
|
||||
function pixelsToMillimeters(pixels) {
|
||||
// 获取当前文档
|
||||
var doc = app.activeDocument;
|
||||
|
||||
// 获取图像的分辨率(像素/英寸)
|
||||
var resolution = doc.resolution;
|
||||
|
||||
// 计算像素转换为毫米
|
||||
var inches = pixels / resolution;
|
||||
var millimeters = inches * 25.4;
|
||||
|
||||
return millimeters.toFixed(2); // 保留两位小数
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 顺时针90旋转() //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d1);
|
||||
d.putUnitDouble(stringIDToTypeID("angle"), stringIDToTypeID("angleUnit"), 90);
|
||||
d.putBoolean(stringIDToTypeID("linked"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 逆时针90旋转() //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d1);
|
||||
d.putUnitDouble(stringIDToTypeID("angle"), stringIDToTypeID("angleUnit"), -90);
|
||||
d.putBoolean(stringIDToTypeID("linked"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 匹配图层名(搜索词) {
|
||||
// 获取指定图层组中的所有图层
|
||||
function 获取组中所有图层(组) {
|
||||
var 图层数组 = [];
|
||||
var 图层组中图层 = 组.layers;
|
||||
|
||||
for (var i = 0; i < 图层组中图层.length; i++) {
|
||||
var 图层 = 图层组中图层[i];
|
||||
图层数组.push(图层);
|
||||
if (图层.typename === "LayerSet") {
|
||||
var 子图层 = 获取组中所有图层(图层);
|
||||
图层数组 = 图层数组.concat(子图层);
|
||||
}
|
||||
}
|
||||
|
||||
return 图层数组;
|
||||
}
|
||||
|
||||
// 获取指定名称的图层组
|
||||
function 根据名称获取图层组(文档, 组名称) {
|
||||
var 组 = null;
|
||||
var 所有图层 = 文档.layers;
|
||||
|
||||
for (var i = 0; i < 所有图层.length; i++) {
|
||||
var 图层 = 所有图层[i];
|
||||
if (图层.typename === "LayerSet" && 图层.name === 组名称) {
|
||||
组 = 图层;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 组;
|
||||
}
|
||||
|
||||
var 文档 = app.activeDocument;
|
||||
var 组名称 = "图层基础信息"; // 指定要匹配的图层组名称
|
||||
var 组 = 根据名称获取图层组(文档, 组名称);
|
||||
|
||||
if (组) {
|
||||
var 图层数组 = 获取组中所有图层(组);
|
||||
var 模糊匹配图层数组 = [];
|
||||
|
||||
// 首先进行模糊匹配
|
||||
for (var i = 0; i < 图层数组.length; i++) {
|
||||
var 图层 = 图层数组[i];
|
||||
if (图层.name.indexOf(搜索词) !== -1) {
|
||||
模糊匹配图层数组.push(图层);
|
||||
}
|
||||
}
|
||||
|
||||
// 在模糊匹配结果中进行图层基础信息数组分割过滤
|
||||
var 精确匹配图层数组 = [];
|
||||
for (var j = 0; j < 模糊匹配图层数组.length; j++) {
|
||||
var 模糊匹配图层 = 模糊匹配图层数组[j];
|
||||
// 进行图层基础信息数组分割过滤
|
||||
var 图层基础信息数组 = 模糊匹配图层.name.split("_"); // 假设分割符是 "_"
|
||||
if (图层基础信息数组[0] === 搜索词) {
|
||||
精确匹配图层数组.push(模糊匹配图层);
|
||||
}
|
||||
}
|
||||
|
||||
// 返回匹配的图层数组
|
||||
return 精确匹配图层数组;
|
||||
} else {
|
||||
alert('未找到名为"' + 组名称 + '"的图层组。');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 毫米转像素(毫米, 每英寸像素数) {
|
||||
var 每英寸毫米数 = 25.4;
|
||||
var 英寸 = 毫米 / 每英寸毫米数;
|
||||
return Math.round(英寸 * 每英寸像素数);
|
||||
}
|
||||
|
||||
function 图层按照缩放定位点进行宽高缩放(缩放定位点的X轴坐标,缩放定位点的Y轴坐标,缩放比例高度) //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSIndependent"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 缩放定位点的X轴坐标);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 缩放定位点的Y轴坐标);
|
||||
d.putObject(stringIDToTypeID("position"), stringIDToTypeID("point"), d1);
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d2.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d2.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d2);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("percentUnit"), 缩放比例高度);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("percentUnit"), 缩放比例高度);
|
||||
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 裁切图层(裁切上边距,裁切左边距,裁切下边距,裁切右边距) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("top"), stringIDToTypeID("pixelsUnit"), 裁切上边距);
|
||||
d1.putUnitDouble(stringIDToTypeID("left"), stringIDToTypeID("pixelsUnit"), 裁切左边距);
|
||||
d1.putUnitDouble(stringIDToTypeID("bottom"), stringIDToTypeID("pixelsUnit"),裁切下边距);
|
||||
d1.putUnitDouble(stringIDToTypeID("right"), stringIDToTypeID("pixelsUnit"), 裁切右边距);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("rectangle"), d1);
|
||||
d.putUnitDouble(stringIDToTypeID("angle"), stringIDToTypeID("angleUnit"), 0);
|
||||
d.putBoolean(stringIDToTypeID("delete"), true);
|
||||
d.putEnumerated(stringIDToTypeID("cropAspectRatioModeKey"), stringIDToTypeID("cropAspectRatioModeClass"), stringIDToTypeID("pureAspectRatio"));
|
||||
d.putBoolean(stringIDToTypeID("constrainProportions"), false);
|
||||
executeAction(stringIDToTypeID("crop"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 获取当前缩放定位点选区四边距() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var selectionBounds = currentDocument.selection.bounds;
|
||||
|
||||
var top2 = selectionBounds[1].value;
|
||||
var left2 = selectionBounds[0].value;
|
||||
var bottom2 = selectionBounds[3].value;
|
||||
var right2 = selectionBounds[2].value;
|
||||
|
||||
return {
|
||||
top2: top2,
|
||||
left2: left2,
|
||||
bottom2: bottom2,
|
||||
right2: right2
|
||||
};
|
||||
}
|
||||
|
||||
function 获取当前选区四边距() {
|
||||
var currentDocument = app.activeDocument;
|
||||
var selectionBounds = currentDocument.selection.bounds;
|
||||
|
||||
var top = selectionBounds[1].value;
|
||||
var left = selectionBounds[0].value;
|
||||
var bottom = selectionBounds[3].value;
|
||||
var right = selectionBounds[2].value;
|
||||
|
||||
return {
|
||||
top: top,
|
||||
left: left,
|
||||
bottom: bottom,
|
||||
right: right
|
||||
};
|
||||
}
|
||||
|
||||
function 历史记录回退缩放函数() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putOffset(stringIDToTypeID("historyState"), -5 );
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 粘贴图层() //粘贴图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("paste"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 复制图层() //复制图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("copyEvent"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 载入选区() //载入选区
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 取消选择() //取消选择
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("to"), stringIDToTypeID("ordinal"), stringIDToTypeID("none"));
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 图层选择(当前图层名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), 当前图层名称);
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(6);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 自由变换() //自由变换
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d1);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("percentUnit"), -100);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("percentUnit"), -100);
|
||||
d.putBoolean(stringIDToTypeID("linked"), true);
|
||||
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||
executeAction(stringIDToTypeID("transform"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 选择上一图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(8);
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 添加图层蒙版() //添加图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("at"), r);
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 应用图层蒙版() //应用图层蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("apply"), true);
|
||||
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 拼合所有蒙版() //拼合所有蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("e805a6ee-6d75-4b62-b6fe-f5873b5fdf20"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 选择蒙版() //选择蒙版
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 历史记录回退() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putOffset(stringIDToTypeID("historyState"), -5);
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function 烧花线添加() {
|
||||
|
||||
app.activeDocument.suspendHistory("烧花线添加", "烧花线()");
|
||||
|
||||
|
||||
function 烧花线() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 遍历当前文档图层
|
||||
|
||||
var doc = app.activeDocument;
|
||||
var layers = doc.layers;
|
||||
var filteredLayers = [];
|
||||
|
||||
// 遍历图层,筛选以P开头的图层
|
||||
for (var i = 0; i < layers.length; i++) {
|
||||
var layer = layers[i];
|
||||
if (layer.name.charAt(0) === 'P') {
|
||||
filteredLayers.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
空置图层()
|
||||
// 输出图层名称
|
||||
for (var j = 0; j < filteredLayers.length; j++) {
|
||||
var filteredLayer = filteredLayers[j];
|
||||
|
||||
var 裁片底图名称=filteredLayer.name;
|
||||
|
||||
多选图层(裁片底图名称);
|
||||
|
||||
// alert(filteredLayer.name);
|
||||
|
||||
}
|
||||
合并图层();
|
||||
置为顶层();
|
||||
画布大小();
|
||||
var layer = app.activeDocument.activeLayer;
|
||||
layer.name = "底图";
|
||||
恢复默认颜色()
|
||||
矩形选框像素点()
|
||||
//色彩范围()
|
||||
填充();
|
||||
魔棒烧花线()
|
||||
新建图层()
|
||||
var layer2 = app.activeDocument.activeLayer;
|
||||
layer2.name = "剪口";
|
||||
扩展2();
|
||||
恢复止口线默认颜色()
|
||||
填充();
|
||||
矩形选框准备删除()
|
||||
清除();
|
||||
魔棒();
|
||||
扩展();
|
||||
选择反向();
|
||||
清除();
|
||||
var 底图 = app.activeDocument.layers.getByName( "底图");
|
||||
app.activeDocument.activeLayer=底图;
|
||||
矩形选框准备删除()
|
||||
清除();
|
||||
置为底层()
|
||||
图层样式()
|
||||
取消选择()
|
||||
|
||||
function 多选图层(裁片底图名称) //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putName(stringIDToTypeID("layer"), 裁片底图名称);
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
|
||||
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||
var list = new ActionList();
|
||||
list.putInteger(4);
|
||||
|
||||
d.putList(stringIDToTypeID("layerID"), list);
|
||||
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 空置图层() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("selectNoLayers"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 恢复止口线默认颜色() //取消选择
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("foregroundColor"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putDouble(stringIDToTypeID("cyan"), 20);
|
||||
d1.putDouble(stringIDToTypeID("magenta"), 0);
|
||||
d1.putDouble(stringIDToTypeID("yellowColor"), 0);
|
||||
d1.putDouble(stringIDToTypeID("black"), 0);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("CMYKColorClass"), d1);
|
||||
d.putString(stringIDToTypeID("source"), "photoshopPicker");
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 合并图层() //合并图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 恢复默认颜色() //
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
executeAction(stringIDToTypeID("reset"), d, DialogModes.NO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 魔棒烧花线() //魔棒
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("point"), d1);
|
||||
d.putInteger(stringIDToTypeID("tolerance"), 6);
|
||||
d.putBoolean(stringIDToTypeID("contiguous"), false);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
function 矩形选框像素点() //矩形选框
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("top"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("left"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("bottom"), stringIDToTypeID("distanceUnit"), 0.48);
|
||||
d1.putUnitDouble(stringIDToTypeID("right"), stringIDToTypeID("distanceUnit"), 0.48);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("rectangle"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 置为底层() //置为底层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("back"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 置为顶层() //置为顶层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var r1 = new ActionReference();
|
||||
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("front"));
|
||||
d.putReference(stringIDToTypeID("to"), r1);
|
||||
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function 色彩范围() //色彩范围
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putInteger(stringIDToTypeID("fuzziness"), 40);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putDouble(stringIDToTypeID("luminance"), 0);
|
||||
d1.putDouble(stringIDToTypeID("a"), 0);
|
||||
d1.putDouble(stringIDToTypeID("b"), 0);
|
||||
d.putObject(stringIDToTypeID("minimum"), stringIDToTypeID("labColor"), d1);
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putDouble(stringIDToTypeID("luminance"), 0);
|
||||
d2.putDouble(stringIDToTypeID("a"), 0);
|
||||
d2.putDouble(stringIDToTypeID("b"), 0);
|
||||
d.putObject(stringIDToTypeID("maximum"), stringIDToTypeID("labColor"), d2);
|
||||
d.putInteger(stringIDToTypeID("colorModel"), 0);
|
||||
executeAction(stringIDToTypeID("colorRange"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 新建图层() //新建图层
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putClass(stringIDToTypeID("layer"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putInteger(stringIDToTypeID("layerID"), 33);
|
||||
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 扩展2() //扩展
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putUnitDouble(stringIDToTypeID("by"), stringIDToTypeID("pixelsUnit"), 1);
|
||||
d.putBoolean(stringIDToTypeID("selectionModifyEffectAtCanvasBounds"), false);
|
||||
executeAction(stringIDToTypeID("expand"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 填充() //填充
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("fillContents"), stringIDToTypeID("foregroundColor"));
|
||||
d.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||
d.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||
executeAction(stringIDToTypeID("fill"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function 画布大小() //画布大小
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 40);
|
||||
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 40);
|
||||
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 魔棒() //魔棒
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 3);
|
||||
d1.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 3);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("point"), d1);
|
||||
d.putInteger(stringIDToTypeID("tolerance"), 6);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
function 矩形选框准备删除() //矩形选框
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("top"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("left"), stringIDToTypeID("distanceUnit"), 0);
|
||||
d1.putUnitDouble(stringIDToTypeID("bottom"), stringIDToTypeID("distanceUnit"), 0.96);
|
||||
d1.putUnitDouble(stringIDToTypeID("right"), stringIDToTypeID("distanceUnit"), 0.96);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("rectangle"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 扩展() //扩展
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
d.putUnitDouble(stringIDToTypeID("by"), stringIDToTypeID("pixelsUnit"), 25);
|
||||
d.putBoolean(stringIDToTypeID("selectionModifyEffectAtCanvasBounds"), false);
|
||||
executeAction(stringIDToTypeID("expand"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 选择反向() //选择反向
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
executeAction(stringIDToTypeID("inverse"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 清除() //清除
|
||||
{
|
||||
app.activeDocument.selection.clear();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 图层样式() //图层样式
|
||||
{
|
||||
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("layerEffects"));
|
||||
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
var d1 = new ActionDescriptor();
|
||||
d1.putUnitDouble(stringIDToTypeID("scale"), stringIDToTypeID("percentUnit"), 12);
|
||||
var d2 = new ActionDescriptor();
|
||||
d2.putBoolean(stringIDToTypeID("enabled"), true);
|
||||
d2.putBoolean(stringIDToTypeID("present"), true);
|
||||
d2.putBoolean(stringIDToTypeID("showInDialog"), true);
|
||||
d2.putEnumerated(stringIDToTypeID("style"), stringIDToTypeID("frameStyle"), stringIDToTypeID("outsetFrame"));
|
||||
d2.putEnumerated(stringIDToTypeID("paintType"), stringIDToTypeID("frameFill"), stringIDToTypeID("solidColor"));
|
||||
d2.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||
d2.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||
d2.putUnitDouble(stringIDToTypeID("size"), stringIDToTypeID("pixelsUnit"), 16);
|
||||
var d3 = new ActionDescriptor();
|
||||
d3.putDouble(stringIDToTypeID("red"), 255);
|
||||
d3.putDouble(stringIDToTypeID("green"), 0);
|
||||
d3.putDouble(stringIDToTypeID("blue"), 0);
|
||||
d2.putObject(stringIDToTypeID("color"), stringIDToTypeID("RGBColor"), d3);
|
||||
d2.putBoolean(stringIDToTypeID("overprint"), false);
|
||||
d1.putObject(stringIDToTypeID("frameFX"), stringIDToTypeID("frameFX"), d2);
|
||||
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layerEffects"), d1);
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function 取消选择() //取消选择
|
||||
{
|
||||
var d = new ActionDescriptor();
|
||||
var r = new ActionReference();
|
||||
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||
d.putReference(stringIDToTypeID("null"), r);
|
||||
d.putEnumerated(stringIDToTypeID("to"), stringIDToTypeID("ordinal"), stringIDToTypeID("none"));
|
||||
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
326
temp_backup/Server_redundant/tempdemo/psmark/Tab1.py
Normal file
@@ -0,0 +1,326 @@
|
||||
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox, QFormLayout, QTabWidget, QPushButton, QLabel, QVBoxLayout, QWidget, QHBoxLayout, QGroupBox, QLineEdit, QCheckBox, QMessageBox
|
||||
from PyQt5.QtGui import QIcon
|
||||
import piece_decorative
|
||||
import re
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
|
||||
class ImportPDFDialog(QMainWindow):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle('Import Multiple PDF pages')
|
||||
self.setWindowIcon(QIcon('icon.png'))
|
||||
|
||||
main_widget = QWidget()
|
||||
self.setCentralWidget(main_widget)
|
||||
main_layout = QVBoxLayout()
|
||||
main_widget.setLayout(main_layout)
|
||||
psd_group = QGroupBox('PSD裁片预处理')
|
||||
psd_layout = QVBoxLayout(psd_group)
|
||||
self.spin_box = QSpinBox()
|
||||
self.spin_box.setMinimum(0)
|
||||
self.spin_box.setMaximum(100)
|
||||
self.spin_box.setValue(1)
|
||||
psd_layout.addWidget(self.spin_box)
|
||||
self.number_button = QPushButton('名称赋予')
|
||||
self.number_button.clicked.connect(self.number_button_clicked)
|
||||
psd_layout.addWidget(self.number_button)
|
||||
form_layout = QFormLayout()
|
||||
# fabric_break_button = QPushButton('设置裁片组')
|
||||
# fabric_break_button.clicked.connect(self.break_fabric_line)
|
||||
# psd_layout.addWidget(fabric_break_button)
|
||||
|
||||
# self.shrink_width_line_edit = QLineEdit()
|
||||
# form_layout.addRow('缩水值宽:', self.shrink_width_line_edit)
|
||||
# self.shrink_height_line_edit = QLineEdit()
|
||||
# form_layout.addRow('缩水值高:', self.shrink_height_line_edit)
|
||||
#
|
||||
# psd_layout.addLayout(form_layout)
|
||||
# psd_pattern_button = QPushButton('缩水修改')
|
||||
# psd_pattern_button.clicked.connect(self.set_psd_pattern)
|
||||
# psd_layout.addWidget(psd_pattern_button)
|
||||
psd_place_button = QPushButton('角度校准')
|
||||
psd_place_button.clicked.connect(self.place_psd_pieces)
|
||||
psd_layout.addWidget(psd_place_button)
|
||||
psdcut_grab_button = QPushButton('花样裁切')
|
||||
psdcut_grab_button.clicked.connect(self.psdcut)
|
||||
psd_layout.addWidget(psdcut_grab_button)
|
||||
psd_grab_button = QPushButton('裁片抓取')
|
||||
psd_grab_button.clicked.connect(self.grab_psd_pieces)
|
||||
psd_layout.addWidget(psd_grab_button)
|
||||
|
||||
main_layout.addWidget(psd_group)
|
||||
|
||||
psd_group2 = QGroupBox('缩水修改')
|
||||
psd_layout = QVBoxLayout(psd_group2)
|
||||
# self.spin_box = QSpinBox()
|
||||
# self.spin_box.setMinimum(0)
|
||||
# self.spin_box.setMaximum(100)
|
||||
# self.spin_box.setValue(1)
|
||||
# psd_layout.addWidget(self.spin_box)
|
||||
# self.number_button = QPushButton('名称赋予')
|
||||
# self.number_button.clicked.connect(self.number_button_clicked)
|
||||
# psd_layout.addWidget(self.number_button)
|
||||
# form_layout = QFormLayout()
|
||||
# fabric_break_button = QPushButton('设置裁片组')
|
||||
# fabric_break_button.clicked.connect(self.break_fabric_line)
|
||||
# psd_layout.addWidget(fabric_break_button)
|
||||
|
||||
self.shrink_width_line_edit = QLineEdit()
|
||||
form_layout.addRow('缩水值宽:', self.shrink_width_line_edit)
|
||||
self.shrink_height_line_edit = QLineEdit()
|
||||
form_layout.addRow('缩水值高:', self.shrink_height_line_edit)
|
||||
|
||||
psd_layout.addLayout(form_layout)
|
||||
psd_pattern_button = QPushButton('缩水修改')
|
||||
psd_pattern_button.clicked.connect(self.set_psd_pattern)
|
||||
psd_layout.addWidget(psd_pattern_button)
|
||||
# psd_place_button = QPushButton('角度校准')
|
||||
# psd_place_button.clicked.connect(self.place_psd_pieces)
|
||||
# psd_layout.addWidget(psd_place_button)
|
||||
# psdcut_grab_button = QPushButton('花样裁切')
|
||||
# psdcut_grab_button.clicked.connect(self.psdcut)
|
||||
# psd_layout.addWidget(psdcut_grab_button)
|
||||
# psd_grab_button = QPushButton('裁片抓取')
|
||||
# psd_grab_button.clicked.connect(self.grab_psd_pieces)
|
||||
# psd_layout.addWidget(psd_grab_button)
|
||||
|
||||
main_layout.addWidget(psd_group2)
|
||||
|
||||
|
||||
|
||||
|
||||
# 添加六个按钮,每行三个按钮
|
||||
align_group = QGroupBox('裁片对齐')
|
||||
align_layout = QVBoxLayout(align_group)
|
||||
|
||||
# 创建水平布局用于放置每行的按钮
|
||||
row_layout = QHBoxLayout()
|
||||
|
||||
# 创建第一个按钮
|
||||
neckline_align_button = QPushButton('左上对齐')
|
||||
neckline_align_button.clicked.connect(self.align_neckline1)
|
||||
row_layout.addWidget(neckline_align_button)
|
||||
|
||||
# 创建第二个按钮
|
||||
button2 = QPushButton('领口对齐')
|
||||
button2.clicked.connect(self.another_function2)
|
||||
row_layout.addWidget(button2)
|
||||
|
||||
# 创建第三个按钮
|
||||
button3 = QPushButton('右上对齐')
|
||||
button3.clicked.connect(self.another_function3)
|
||||
row_layout.addWidget(button3)
|
||||
|
||||
# 将第一行按钮添加到垂直布局
|
||||
align_layout.addLayout(row_layout)
|
||||
|
||||
# 创建水平布局用于放置第二行的按钮
|
||||
row_layout = QHBoxLayout()
|
||||
|
||||
# 创建第四个按钮
|
||||
button4 = QPushButton('左下对齐')
|
||||
button4.clicked.connect(self.another_function4)
|
||||
row_layout.addWidget(button4)
|
||||
|
||||
# 创建第五个按钮
|
||||
button5 = QPushButton('下摆对齐')
|
||||
button5.clicked.connect(self.another_function5)
|
||||
row_layout.addWidget(button5)
|
||||
|
||||
# 创建第六个按钮
|
||||
button6 = QPushButton('右下对齐')
|
||||
button6.clicked.connect(self.another_function6)
|
||||
row_layout.addWidget(button6)
|
||||
|
||||
# 将第二行按钮添加到垂直布局
|
||||
align_layout.addLayout(row_layout)
|
||||
|
||||
main_layout.addWidget(align_group)
|
||||
|
||||
main_layout.addWidget(align_group)
|
||||
info_group = QGroupBox('信息写入')
|
||||
info_layout = QVBoxLayout(info_group)
|
||||
size_add_button = QPushButton('添加定位点')
|
||||
size_add_button.clicked.connect(self.add_sizes)
|
||||
info_layout.addWidget(size_add_button)
|
||||
# material_count_button = QPushButton('缩放信息写入')
|
||||
# material_count_button.clicked.connect(self.count_materials)
|
||||
# info_layout.addWidget(material_count_button)
|
||||
|
||||
material_count_button2 = QPushButton('重写缩放信息')
|
||||
material_count_button2.clicked.connect(self.count_materials2)
|
||||
info_layout.addWidget(material_count_button2)
|
||||
|
||||
|
||||
|
||||
main_layout.addWidget(info_group)
|
||||
pattern_group = QGroupBox('自动套花')
|
||||
pattern_layout = QVBoxLayout(pattern_group)
|
||||
pattern_extend_button = QPushButton('通码延申')
|
||||
pattern_extend_button.clicked.connect(self.extend_pattern)
|
||||
pattern_layout.addWidget(pattern_extend_button)
|
||||
scale_button = QPushButton('宽高缩放')
|
||||
scale_button.clicked.connect(self.scale_dimensions)
|
||||
pattern_layout.addWidget(scale_button)
|
||||
proportional_scale_button = QPushButton('比例缩放')
|
||||
proportional_scale_button.clicked.connect(self.proportional_scale)
|
||||
pattern_layout.addWidget(proportional_scale_button)
|
||||
|
||||
bigproportional_scale_button = QPushButton('定位点比例缩放')
|
||||
bigproportional_scale_button.clicked.connect(self.bigproportional_scale)
|
||||
pattern_layout.addWidget(bigproportional_scale_button)
|
||||
|
||||
# self.checkbox1 = QCheckBox('混排套图方法', self)
|
||||
# pattern_layout.addWidget(self.checkbox1)
|
||||
|
||||
main_layout.addWidget(pattern_group)
|
||||
save_group = QGroupBox('文档保存')
|
||||
save_layout = QVBoxLayout(save_group)
|
||||
# add_size_button = QPushButton('尺码激活')
|
||||
# add_size_button.clicked.connect(self.add_size_to_layers)
|
||||
# save_layout.addWidget(add_size_button)
|
||||
form_layout = QFormLayout()
|
||||
self.prefix_line_edit = QLineEdit()
|
||||
form_layout.addRow('前缀添加:', self.prefix_line_edit)
|
||||
save_layout.addLayout(form_layout)
|
||||
save_button = QPushButton('保存')
|
||||
save_button.clicked.connect(self.save_document2)
|
||||
save_layout.addWidget(save_button)
|
||||
main_layout.addWidget(save_group)
|
||||
|
||||
|
||||
def align_neckline1(self):
|
||||
|
||||
piece_decorative.PS_DXF7_jscode_fun('左上对齐2()')
|
||||
pass
|
||||
|
||||
def another_function2(self):
|
||||
|
||||
piece_decorative.PS_DXF3_jscode_fun('领口对齐2()')
|
||||
pass
|
||||
|
||||
def another_function3(self):
|
||||
piece_decorative.PS_DXF2_jscode_fun('右上对齐2()')
|
||||
# piece_decorative.PS_DXF2_jscode_fun(f'文档保存最新("{前缀}");')
|
||||
pass
|
||||
|
||||
|
||||
def another_function4(self):
|
||||
piece_decorative.PS_DXF4_jscode_fun('左下对齐2()')
|
||||
# piece_decorative.PS_DXF2_jscode_fun(f'文档保存最新("{前缀}");')
|
||||
pass
|
||||
|
||||
def another_function5(self):
|
||||
piece_decorative.PS_DXF5_jscode_fun('下摆对齐2()')
|
||||
# piece_decorative.PS_DXF2_jscode_fun(f'文档保存最新("{前缀}");')
|
||||
pass
|
||||
|
||||
def another_function6(self):
|
||||
piece_decorative.PS_DXF6_jscode_fun('右下对齐2()')
|
||||
# piece_decorative.PS_DXF2_jscode_fun(f'文档保存最新("{前缀}");')
|
||||
pass
|
||||
|
||||
def save_document2(self):
|
||||
前缀 = self.prefix_line_edit.text()
|
||||
piece_decorative.PS_DXF2_jscode_fun(f'文档保存最新("{前缀}");')
|
||||
|
||||
|
||||
def add_size_to_layers(self):
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
|
||||
|
||||
def psdcut(self):
|
||||
piece_decorative.PS_DXF6_jscode_fun('图像切割2();')
|
||||
|
||||
def number_button_clicked(self):
|
||||
current_value = self.spin_box.value()
|
||||
piece_decorative.PS_DXF_jscode_fun(f"名称赋予({current_value});")
|
||||
new_value = current_value + 1
|
||||
self.spin_box.setValue(new_value)
|
||||
|
||||
def another_function(self):
|
||||
# 在这里定义按钮点击事件的处理逻辑
|
||||
pass
|
||||
def set_psd_pattern(self):
|
||||
高度值 = self.shrink_height_line_edit.text()
|
||||
宽度值 = self.shrink_width_line_edit.text()
|
||||
|
||||
number_pattern = re.compile(r'^\d+(\.\d+)?$') # 正则表达式匹配数字格式
|
||||
|
||||
if number_pattern.match(高度值) and number_pattern.match(宽度值):
|
||||
# 如果两个值都是数字,执行批量缩水操作
|
||||
# piece_decorative.PS_DXF2_jscode_fun('设置花样组删除图层设置名称();')
|
||||
piece_decorative.PS_DXF2_jscode_fun(f"批量缩水值修改({宽度值},{高度值});")
|
||||
else:
|
||||
# 如果至少有一个值不是数字,显示警告
|
||||
QMessageBox.warning(self, '错误', '缩水值只能是数字!')
|
||||
|
||||
def grab_psd_pieces(self):
|
||||
piece_decorative.PS_DXF2_jscode_fun('设置花样组删除图层设置名称();')
|
||||
piece_decorative.PS_DXF12_jscode_fun('批量套数写入();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('设置花样组2();')
|
||||
piece_decorative.PS_DXF_jscode_fun('裁片吸取2();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('设置花样组顺序居中();')
|
||||
piece_decorative.PS_DXF_jscode_fun('信息写入();')
|
||||
piece_decorative.PS_DXF3_jscode_fun('裁片视图检查2();')
|
||||
|
||||
def place_psd_pieces(self):
|
||||
piece_decorative.PS_DXF3_jscode_fun('角度旋转();')
|
||||
|
||||
def align_neckline(self):
|
||||
piece_decorative.PS_DXF2_jscode_fun('领口对齐();')
|
||||
|
||||
|
||||
def extend_pattern(self):
|
||||
|
||||
# if self.checkbox1.isChecked():
|
||||
# piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
# piece_decorative.PS_DXF20_jscode_fun('混排通码延申导出();')
|
||||
# print('按下')
|
||||
#
|
||||
# else:
|
||||
# print('没有按下')
|
||||
piece_decorative.PS_DXF6_jscode_fun('前景色修改();')
|
||||
piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
piece_decorative.PS_DXF_jscode_fun('裁片射出();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
|
||||
def scale_dimensions(self):
|
||||
piece_decorative.PS_DXF6_jscode_fun('前景色修改();')
|
||||
piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
piece_decorative.PS_DXF3_jscode_fun('裁片射出宽高缩放();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
|
||||
def proportional_scale(self):
|
||||
piece_decorative.PS_DXF6_jscode_fun('前景色修改();')
|
||||
piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
piece_decorative.PS_DXF9_jscode_fun('裁片射出比例缩放按中心点();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
|
||||
def bigproportional_scale(self):
|
||||
piece_decorative.PS_DXF6_jscode_fun('前景色修改();')
|
||||
piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
piece_decorative.PS_DXF11_jscode_fun('裁片射出缩放();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
|
||||
|
||||
|
||||
def add_sizes(self):
|
||||
piece_decorative.PS_DXF_jscode_fun('添加缩放定位点();')
|
||||
|
||||
def count_materials(self):
|
||||
piece_decorative.PS_DXF_jscode_fun('信息写入();')
|
||||
|
||||
def count_materials2(self):
|
||||
piece_decorative.PS_DXF3_jscode_fun('重写基码信息2();')
|
||||
piece_decorative.PS_DXF_jscode_fun('信息写入();')
|
||||
|
||||
if __name__ == '__main__':
|
||||
app2 = QApplication(sys.argv)
|
||||
dialog = ImportPDFDialog()
|
||||
dialog.show()
|
||||
sys.exit(app2.exec_())
|
||||
427
temp_backup/Server_redundant/tempdemo/psmark/Tab2.py
Normal file
@@ -0,0 +1,427 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QLabel, QPushButton, QFileDialog, \
|
||||
QLineEdit, QScrollArea, QGroupBox, QHBoxLayout, QMessageBox, QProgressDialog
|
||||
from PyQt5.QtGui import QIntValidator
|
||||
from PyQt5.QtCore import QTimer
|
||||
import re
|
||||
import os
|
||||
import ezdxf
|
||||
import ezdxf.tools
|
||||
import ezdxf.bbox
|
||||
import ezdxf.units
|
||||
import ezdxf.math
|
||||
from coreldraw_checker import is_coreldraw_running
|
||||
from functools import partial
|
||||
import win32com.client
|
||||
import os
|
||||
import shutil
|
||||
import threading
|
||||
from clear_folder import another_function
|
||||
|
||||
|
||||
class YourMainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.setWindowTitle("RUNDXF")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
self.button1 = QPushButton("DXF文件路径")
|
||||
self.button1.setFixedWidth(200)
|
||||
self.button1.clicked.connect(self.showDxfFileDialog)
|
||||
|
||||
self.button2 = QPushButton("PLT文件路径")
|
||||
self.button2.setFixedWidth(200)
|
||||
self.button2.clicked.connect(self.showPltFileDialog)
|
||||
|
||||
self.initial_button1_text = self.button1.text()
|
||||
self.initial_button2_text = self.button2.text()
|
||||
|
||||
panel1 = QGroupBox("文件路径")
|
||||
panel1_layout = QVBoxLayout(panel1)
|
||||
panel1_layout.setSpacing(10)
|
||||
panel1_layout.setContentsMargins(10, 10, 10, 10)
|
||||
panel1_layout.addWidget(self.button1)
|
||||
panel1_layout.addWidget(self.button2)
|
||||
layout.addWidget(panel1)
|
||||
|
||||
self.label5 = QLabel('单码片数')
|
||||
self.lineEdit3 = QLineEdit()
|
||||
self.lineEdit3.setValidator(QIntValidator())
|
||||
self.lineEdit3.setFixedSize(120, 30)
|
||||
layout.addWidget(self.label5)
|
||||
layout.addWidget(self.lineEdit3)
|
||||
|
||||
self.scrollWidget = QWidget()
|
||||
self.scrollWidgetLayout = QVBoxLayout(self.scrollWidget)
|
||||
self.scrollArea = QScrollArea()
|
||||
self.scrollArea.setWidget(self.scrollWidget)
|
||||
self.scrollArea.setWidgetResizable(True)
|
||||
layout.addWidget(self.scrollArea)
|
||||
|
||||
self.clearButton = QPushButton("清空信息")
|
||||
self.clearButton.clicked.connect(self.clearScrollArea)
|
||||
layout.addWidget(self.clearButton)
|
||||
|
||||
confirm_button = QPushButton("分割")
|
||||
confirm_button.clicked.connect(self.updateScrollArea)
|
||||
#confirm_button.clicked.connect(self.freezeAndParse) # 连接按钮点击事件
|
||||
layout.addWidget(confirm_button)
|
||||
|
||||
|
||||
central_widget = QWidget()
|
||||
central_widget.setLayout(layout)
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
self.dxfLineEdits = {}
|
||||
|
||||
self.allowButtonActions = True # 标志变量,控制是否允许按钮行为
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def run_coreldraw_macros(self):
|
||||
try:
|
||||
dogms = win32com.client.DispatchEx("CorelDRAW.Application.23")
|
||||
|
||||
macros_to_run = [
|
||||
"RUN.OpenDXFFilesInFolder",
|
||||
"RUN.RotateSelectionClockwise",
|
||||
"RUN.DeleteUnnamedSublayers",
|
||||
"RUN.StToFront",
|
||||
"RUN.IterateSublayerNames",
|
||||
|
||||
|
||||
]
|
||||
|
||||
for macro in macros_to_run:
|
||||
dogms.GMSManager.RunMacro("RUNDXF", macro)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "警告", "缺少CDR模块,请载入CDR模块", QMessageBox.Ok)
|
||||
# print("缺少CDR模块,请载入CDR模块")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def showPltFileDialog(self):
|
||||
options = QFileDialog.Options()
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, "选择PLT文件", "", "PLT Files (*.plt);;All Files (*)",
|
||||
options=options)
|
||||
if file_path:
|
||||
print("Selected PLT file:", file_path)
|
||||
extracted_content = self.extract_content_from_plt_path(file_path)
|
||||
print(extracted_content)
|
||||
self.sizes = self.fill_sizes_from_extracted_content(extracted_content)
|
||||
|
||||
# 更新尺寸字典后,清空并填充滚动区域
|
||||
self.clearScrollArea()
|
||||
self.populateScrollArea()
|
||||
|
||||
self.initial_plt_path = file_path # 更新初始路径而不更新按钮文本
|
||||
|
||||
else:
|
||||
print("No PLT file selected")
|
||||
QMessageBox.warning(self, "警告", "没有选择文件夹。请重新选择文件夹。", QMessageBox.Ok)
|
||||
|
||||
pass
|
||||
|
||||
def showDxfFileDialog(self):
|
||||
options = QFileDialog.Options()
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, "选择DXF文件", "", "DXF Files (*.dxf);;All Files (*)",
|
||||
options=options)
|
||||
if file_path:
|
||||
print("Selected DXF file:", file_path)
|
||||
self.initial_dxf_path = file_path # 更新初始路径而不更新按钮文本
|
||||
else:
|
||||
print("No DXF file selected")
|
||||
QMessageBox.warning(self, "警告", "没有选择文件夹。请重新选择文件夹。", QMessageBox.Ok)
|
||||
# 在此处添加提醒逻辑,例如使用 QMessageBox 提示用户没有选择文件
|
||||
|
||||
def extract_content_from_plt_path(self, plt_path):
|
||||
match = re.search(r'\((.*?)\)', plt_path)
|
||||
if match:
|
||||
extracted_content = match.group(1)
|
||||
return extracted_content
|
||||
else:
|
||||
return "No content in parentheses found"
|
||||
|
||||
def fill_sizes_from_extracted_content(self, extracted_content):
|
||||
sizes = extracted_content.split("+")
|
||||
size_dict = {}
|
||||
for size in sizes:
|
||||
size_dict[size] = ""
|
||||
return size_dict
|
||||
|
||||
def process_dxf_file(self, file_path, extracted_content):
|
||||
doc = ezdxf.readfile(file_path)
|
||||
msp = doc.modelspace()
|
||||
mspBox = ezdxf.bbox.extents(msp)
|
||||
|
||||
print("=====", os.path.basename(file_path))
|
||||
print("左上角坐标:", mspBox.extmin)
|
||||
print("画布宽:", mspBox.size[0], "画布高:", mspBox.size[1])
|
||||
print()
|
||||
|
||||
for entity in msp.query():
|
||||
if entity.dxftype() == "INSERT":
|
||||
temp = []
|
||||
rotation = None
|
||||
block = doc.blocks[entity.dxf.name]
|
||||
|
||||
for e in block:
|
||||
if e.dxftype() != "TEXT":
|
||||
temp.append(e)
|
||||
else:
|
||||
rotation = e.dxf.rotation
|
||||
|
||||
if rotation is not None:
|
||||
rotation %= 360
|
||||
if 45 <= rotation < 135:
|
||||
rotation = 90
|
||||
elif 135 <= rotation < 225:
|
||||
rotation = 180
|
||||
elif 225 <= rotation < 315:
|
||||
rotation = -90
|
||||
else:
|
||||
rotation = 0
|
||||
|
||||
print("=====", entity.dxf.name)
|
||||
print("大小:", ezdxf.bbox.extents(temp).size)
|
||||
print("文字角度:", rotation)
|
||||
|
||||
center = ezdxf.bbox.extents(temp).center
|
||||
center = (center.x, mspBox.extmax.y - center.y) # 调整center y值
|
||||
|
||||
print("中心坐标:", center)
|
||||
|
||||
center = (center[1], mspBox.size[0] - center[0]) # 旋转后中心坐标
|
||||
print("旋转后中心坐标:", center)
|
||||
|
||||
separator = "_" # 分隔符
|
||||
entity.dxf.name += separator + str(rotation)
|
||||
block.name += separator + str(rotation)
|
||||
|
||||
new_file_path = os.path.join(r"D:\marktemp", "{}.dxf".format(extracted_content))
|
||||
doc.saveas(new_file_path)
|
||||
def getSinglePieceCount(self):
|
||||
return self.lineEdit3.text()
|
||||
|
||||
def recreate_folders(self):
|
||||
# 定义文件夹路径
|
||||
folder_paths = [r"D:\PSMARKtemp", r"D:\marktemp"]
|
||||
|
||||
# 删除文件夹及其内容
|
||||
for folder_path in folder_paths:
|
||||
if os.path.exists(folder_path):
|
||||
shutil.rmtree(folder_path)
|
||||
print(f"Deleted folder: {folder_path}")
|
||||
|
||||
# 重新创建文件夹
|
||||
for folder_path in folder_paths:
|
||||
os.makedirs(folder_path)
|
||||
print(f"Recreated folder: {folder_path}")
|
||||
|
||||
def freezeAndParse(self):
|
||||
self.parse_button.setEnabled(False) # 冻结按钮
|
||||
QTimer.singleShot(10000, self.unfreezeButton) # 10秒后解冻按钮
|
||||
|
||||
def unfreezeButton(self):
|
||||
self.parse_button.setEnabled(True) # 解冻按钮
|
||||
|
||||
|
||||
def updateScrollArea(self):
|
||||
|
||||
another_function()
|
||||
|
||||
if not is_coreldraw_running():
|
||||
QMessageBox.warning(self, "警告", "CorelDRAW未运行,无法执行操作。")
|
||||
return
|
||||
|
||||
plt_file_path = self.initial_plt_path
|
||||
|
||||
# 获取DXF文件路径
|
||||
dxf_file_path = self.initial_dxf_path
|
||||
extracted_content = self.extract_content_from_plt_path(plt_file_path)
|
||||
|
||||
if dxf_file_path:
|
||||
# 去掉括号内内容后的PLT文件名作为DXF文件名
|
||||
plt_filename = os.path.basename(plt_file_path)
|
||||
# dxf_filename = re.sub(r'\(.*?\)', '', plt_filename)
|
||||
self.process_dxf_file(dxf_file_path, extracted_content) # 调用解析函数并传入单码片数和新的DXF文件名
|
||||
print("DXF文件解析完成!")
|
||||
else:
|
||||
QMessageBox.warning(self, "警告", "没有选择DXF文件。请先选择一个DXF文件。", QMessageBox.Ok)
|
||||
print()
|
||||
|
||||
|
||||
|
||||
self.run_coreldraw_macros()
|
||||
single_code_pieces = int(self.getSinglePieceCount()) # 获取单码片数
|
||||
print(single_code_pieces)
|
||||
# 打印滚动区域中的输入框内容
|
||||
code_quantities = {} # 创建一个新的字典用于存储数据
|
||||
|
||||
for label, line_edit in self.dxfLineEdits.items():
|
||||
text = line_edit.text()
|
||||
if text.isdigit():
|
||||
value = int(text) # 尝试将文本转换为整数
|
||||
else:
|
||||
try:
|
||||
value = float(text) # 尝试将文本转换为浮点数
|
||||
except ValueError:
|
||||
print(f"Invalid value for {label}: {text}")
|
||||
continue # 转换失败,跳过当前循环迭代
|
||||
|
||||
code_quantities[label] = value # 存储转换后的数字到字典
|
||||
|
||||
print(code_quantities)
|
||||
length = len(code_quantities)
|
||||
print(length) # 输出 3,因为字典中有三对键值对
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
corel_app = win32com.client.Dispatch("CorelDRAW.Application.23")
|
||||
|
||||
# 获取当前活动文档
|
||||
active_document = corel_app.ActiveDocument
|
||||
|
||||
# 获取当前页面中所有图层的名称,排除特定名称的图层
|
||||
layer_names = [layer.Name for layer in active_document.ActivePage.Layers
|
||||
if layer.Name not in ["辅助线", "1", "0", "Defpoints"]]
|
||||
|
||||
p_numbers = [] # 初始化存储 P 数字的列表
|
||||
|
||||
for code, quantity in code_quantities.items():
|
||||
for i in range(1, single_code_pieces + 1):
|
||||
p_numbers.extend([f"P{i}"] * int(quantity))
|
||||
|
||||
print(p_numbers)
|
||||
|
||||
|
||||
# 循环遍历不同的码
|
||||
# p_numbers = [] # 初始化存储 P 数字的列表
|
||||
#
|
||||
# for code, quantity in code_quantities.items():
|
||||
# for i in range(1, single_code_pieces + 1):
|
||||
# # 根据码的数量分别生成对应数量的 P 数字,并添加到列表中
|
||||
# p_numbers.extend([f"P{i}"] * quantity)
|
||||
# print(p_numbers)
|
||||
new_layer_names = []
|
||||
index = 0
|
||||
for old_name in layer_names:
|
||||
parts = old_name.split("-") # 根据"-"分割字符串
|
||||
if len(parts) > 1:
|
||||
new_name = f"{p_numbers[index]}-{parts[1]}" # 使用数组中的 P 数字
|
||||
new_layer_names.append(new_name)
|
||||
index += 1
|
||||
|
||||
# 在新的图层名数组中遍历,对图层进行修改
|
||||
modified_names = [] # 创建一个列表来存储修改后的名称
|
||||
modified_names2 = []
|
||||
for i, new_name in enumerate(new_layer_names):
|
||||
active_document.ActivePage.Layers(layer_names[i]).Name = new_name
|
||||
modified_names2.append(new_name)
|
||||
modified_names.append(new_name)
|
||||
print(f"Modified: {layer_names[i]} -> {new_name}")
|
||||
|
||||
|
||||
|
||||
# print(modified_names)
|
||||
|
||||
|
||||
def delete_layers_by_names(names_to_delete, active_document):
|
||||
corel_app = win32com.client.Dispatch("CorelDRAW.Application.23")
|
||||
|
||||
# 获取当前活动文档
|
||||
active_document = corel_app.ActiveDocument
|
||||
for target_layer_name in names_to_delete:
|
||||
for layer in active_document.ActivePage.Layers:
|
||||
if layer.Name == target_layer_name:
|
||||
layer.Delete()
|
||||
break # 找到目标图层后中断循环
|
||||
|
||||
|
||||
|
||||
modified_names_list = [] # 用于存储每次循环中的 modified_names 列表
|
||||
|
||||
Index = 0
|
||||
for code in code_quantities:
|
||||
quantity = code_quantities[code]
|
||||
total_pieces = quantity * single_code_pieces
|
||||
|
||||
# 获取数组的前 total_pieces 个元素
|
||||
newmodified_names_filtered = modified_names[:total_pieces]
|
||||
# print(newmodified_names_filtered)
|
||||
|
||||
result_array = [fruit for fruit in modified_names2 if fruit not in newmodified_names_filtered]
|
||||
result_array_length = len(result_array)
|
||||
print(result_array_length)
|
||||
|
||||
delete_layers_by_names(result_array, active_document.ActivePage)
|
||||
# modified_names_list.append(modified_names) # 将 modified_names 添加到数组中
|
||||
|
||||
|
||||
dogms = win32com.client.DispatchEx("CorelDRAW.Application.23")
|
||||
|
||||
dogms.GMSManager.RunMacro("RUNDXF", "RUN.ExportSelectionToPSD", Index)
|
||||
|
||||
dogms.GMSManager.RunMacro("RUNDXF", "RUN.HOURUN", result_array_length)
|
||||
|
||||
modified_names = modified_names[total_pieces:]
|
||||
|
||||
Index += 1
|
||||
|
||||
|
||||
corel_app = win32com.client.Dispatch("CorelDRAW.Application.23")
|
||||
|
||||
corel_app .GMSManager.RunMacro("RUNDXF", "RUN.ActiveDocumentClose")
|
||||
|
||||
QMessageBox.warning(self, "提醒", "分割完成,请进行裁片套版操作。")
|
||||
|
||||
def updateLineEditsFromSizes(self):
|
||||
for size_label, line_edit in self.dxfLineEdits.items():
|
||||
self.sizes[size_label] = line_edit.text()
|
||||
|
||||
def populateScrollArea(self):
|
||||
self.clearScrollArea()
|
||||
for size_label, size_text in self.sizes.items():
|
||||
size_layout = QHBoxLayout()
|
||||
size_layout.addWidget(QLabel(size_label))
|
||||
|
||||
line_edit = QLineEdit()
|
||||
line_edit.setValidator(QIntValidator())
|
||||
line_edit.setFixedSize(100, 30)
|
||||
line_edit.setText(size_text)
|
||||
|
||||
self.dxfLineEdits[size_label] = line_edit
|
||||
size_layout.addWidget(line_edit)
|
||||
|
||||
self.scrollWidgetLayout.addLayout(size_layout)
|
||||
|
||||
def clearScrollArea(self):
|
||||
for i in reversed(range(self.scrollWidgetLayout.count())):
|
||||
item = self.scrollWidgetLayout.itemAt(i)
|
||||
if isinstance(item, QHBoxLayout) or isinstance(item, QVBoxLayout):
|
||||
while item.count():
|
||||
widget = item.takeAt(0).widget()
|
||||
if widget:
|
||||
widget.deleteLater()
|
||||
self.dxfLineEdits.clear() # 清空部件引用
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
mainWindow = YourMainWindow()
|
||||
mainWindow.show()
|
||||
sys.exit(app.exec_())
|
||||
311
temp_backup/Server_redundant/tempdemo/psmark/Tab3.py
Normal file
@@ -0,0 +1,311 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout, QPushButton, QGroupBox, QLabel, QLineEdit, QFormLayout
|
||||
import piece_decorative
|
||||
|
||||
class ImportPDFDialog2(QMainWindow):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle('Import Multiple PDF pages')
|
||||
|
||||
main_widget = QWidget()
|
||||
self.setCentralWidget(main_widget)
|
||||
main_layout = QVBoxLayout()
|
||||
main_widget.setLayout(main_layout)
|
||||
|
||||
|
||||
new_group_box5 = QGroupBox('打版联动')
|
||||
new_group_layout5 = QVBoxLayout(new_group_box5)
|
||||
|
||||
# 创建4个按钮并连接到槽函数
|
||||
Dbtn4_1 = QPushButton('图层分割')
|
||||
Dbtn4_2 = QPushButton('批量图层编组')
|
||||
Dbtn4_3 = QPushButton('快速超链接')
|
||||
# Dbtn4_4 = QPushButton('定位点比例缩放(模板)')
|
||||
|
||||
Dbtn4_1.clicked.connect(self.on_Dbtn4_1_clicked)
|
||||
Dbtn4_2.clicked.connect(self.on_Dbtn4_2_clicked)
|
||||
Dbtn4_3.clicked.connect(self.on_Dbtn4_3_clicked)
|
||||
# Dbtn4_4.clicked.connect(self.on_Dbtn4_4_clicked)
|
||||
|
||||
# 将按钮添加到新的盒子1中
|
||||
new_group_layout5.addWidget(Dbtn4_1)
|
||||
new_group_layout5.addWidget(Dbtn4_2)
|
||||
new_group_layout5.addWidget(Dbtn4_3)
|
||||
# new_group_layout5.addWidget(Dbtn4_4)
|
||||
# 将新的盒子1添加到主布局中
|
||||
main_layout.addWidget(new_group_box5)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 快速换图
|
||||
quick_change_group = QGroupBox('快速换图')
|
||||
quick_change_layout = QVBoxLayout(quick_change_group)
|
||||
# bigbtn_standardize_pattern = QPushButton('图像切割')
|
||||
btn_standardize_pattern = QPushButton('花样标准化')
|
||||
|
||||
btn_pattern_to_external = QPushButton('花样转外链')
|
||||
btn_quick_change = QPushButton('快速换图')
|
||||
btn_batch_quick_change = QPushButton('批量快速换图')
|
||||
# btn_Kbatch_quick_change = QPushButton('特定版本快速换图')
|
||||
# 为每个按钮连接槽函数
|
||||
btn_standardize_pattern.clicked.connect(self.on_standardize_pattern_clicked)
|
||||
btn_pattern_to_external.clicked.connect(self.on_pattern_to_external_clicked)
|
||||
btn_quick_change.clicked.connect(self.on_quick_change_clicked)
|
||||
btn_batch_quick_change.clicked.connect(self.on_batch_quick_change_clicked)
|
||||
# btn_Kbatch_quick_change.clicked.connect(self.on_kbatch_quick_change_clicked)
|
||||
# bigbtn_standardize_pattern.clicked.connect(self.on_bigstandardize_pattern_clicked)
|
||||
|
||||
quick_change_layout.addWidget(btn_standardize_pattern)
|
||||
# quick_change_layout.addWidget(bigbtn_standardize_pattern)
|
||||
quick_change_layout.addWidget(btn_pattern_to_external)
|
||||
quick_change_layout.addWidget(btn_quick_change)
|
||||
quick_change_layout.addWidget(btn_batch_quick_change)
|
||||
# quick_change_layout.addWidget(btn_Kbatch_quick_change)
|
||||
|
||||
main_layout.addWidget(quick_change_group)
|
||||
|
||||
# 初始化界面
|
||||
|
||||
# 创建一个新的盒子
|
||||
new_group_box1 = QGroupBox('模板生成')
|
||||
new_group_layout1 = QVBoxLayout(new_group_box1)
|
||||
|
||||
# 创建4个按钮并连接到槽函数
|
||||
btn4_1 = QPushButton('通码延申(模板)')
|
||||
btn4_2 = QPushButton('宽高缩放(模板)')
|
||||
btn4_3 = QPushButton('比例缩放(模板)')
|
||||
btn4_4 = QPushButton('定位点比例缩放(模板)')
|
||||
|
||||
btn4_1.clicked.connect(self.on_btn4_1_clicked)
|
||||
btn4_2.clicked.connect(self.on_btn4_2_clicked)
|
||||
btn4_3.clicked.connect(self.on_btn4_3_clicked)
|
||||
btn4_4.clicked.connect(self.on_btn4_4_clicked)
|
||||
|
||||
# 将按钮添加到新的盒子1中
|
||||
new_group_layout1.addWidget(btn4_1)
|
||||
new_group_layout1.addWidget(btn4_2)
|
||||
new_group_layout1.addWidget(btn4_3)
|
||||
new_group_layout1.addWidget(btn4_4)
|
||||
# 将新的盒子1添加到主布局中
|
||||
main_layout.addWidget(new_group_box1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#############################################
|
||||
|
||||
new_group_box5 = QGroupBox('定位码快速换图')
|
||||
new_group_layout5 = QVBoxLayout(new_group_box5)
|
||||
|
||||
# 创建4个按钮并连接到槽函数
|
||||
Kbtn4_1 = QPushButton('定位码快速超链接')
|
||||
Kbtn4_2 = QPushButton('定位码快速换图')
|
||||
|
||||
# Dbtn4_4 = QPushButton('定位点比例缩放(模板)')
|
||||
|
||||
Kbtn4_1.clicked.connect(self.on_Kbtn4_1_clicked)
|
||||
Kbtn4_2.clicked.connect(self.on_Kbtn4_2_clicked)
|
||||
#Dbtn4_3.clicked.connect(self.on_Dbtn4_3_clicked)
|
||||
# Dbtn4_4.clicked.connect(self.on_Dbtn4_4_clicked)
|
||||
|
||||
# 将按钮添加到新的盒子1中
|
||||
new_group_layout5.addWidget(Kbtn4_1)
|
||||
new_group_layout5.addWidget(Kbtn4_2)
|
||||
# new_group_layout5.addWidget(Dbtn4_3)
|
||||
# new_group_layout5.addWidget(Dbtn4_4)
|
||||
# 将新的盒子1添加到主布局中
|
||||
main_layout.addWidget(new_group_box5)
|
||||
|
||||
|
||||
|
||||
##############################################
|
||||
|
||||
new_group_box2 = QGroupBox('批量化工具')
|
||||
new_group_layout2 = QVBoxLayout(new_group_box2)
|
||||
|
||||
# 创建4个按钮并连接到槽函数
|
||||
Pbtn4_1 = QPushButton('小码标添加')
|
||||
Pbtn4_2 = QPushButton('批量修改分辨率')
|
||||
Pbtn4_3 = QPushButton('批量加款号')
|
||||
Pbtn4_4 = QPushButton('模特批量替换')
|
||||
Pbtn4_5 = QPushButton('SO小样连晒')
|
||||
Pbtn4_6 = QPushButton('SO小样拼贴')
|
||||
Pbtn4_7 = QPushButton('SO小样缩放')
|
||||
|
||||
Pbtn4_1.clicked.connect(self.on_Pbtn4_1_clicked)
|
||||
Pbtn4_2.clicked.connect(self.on_Pbtn4_2_clicked)
|
||||
Pbtn4_3.clicked.connect(self.on_Pbtn4_3_clicked)
|
||||
Pbtn4_4.clicked.connect(self.on_Pbtn4_4_clicked)
|
||||
Pbtn4_5.clicked.connect(self.on_Pbtn4_5_clicked)
|
||||
Pbtn4_6.clicked.connect(self.on_Pbtn4_6_clicked)
|
||||
Pbtn4_7.clicked.connect(self.on_Pbtn4_7_clicked)
|
||||
# 将按钮添加到新的盒子1中
|
||||
new_group_layout2.addWidget(Pbtn4_1)
|
||||
new_group_layout2.addWidget(Pbtn4_2)
|
||||
new_group_layout2.addWidget(Pbtn4_3)
|
||||
new_group_layout2.addWidget(Pbtn4_4)
|
||||
new_group_layout2.addWidget(Pbtn4_5)
|
||||
new_group_layout2.addWidget(Pbtn4_6)
|
||||
new_group_layout2.addWidget(Pbtn4_7)
|
||||
# 将新的盒子1添加到主布局中
|
||||
main_layout.addWidget(new_group_box2)
|
||||
|
||||
# def on_kbatch_quick_change_clicked(self):
|
||||
# piece_decorative.PS_DXF18_jscode_fun('龙服的快速换图();')
|
||||
#
|
||||
# print("按钮被点击")
|
||||
# pass
|
||||
|
||||
def on_Pbtn4_7_clicked(self):
|
||||
piece_decorative.PS_DXF27_jscode_fun('新的米样缩放();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
|
||||
def on_Kbtn4_1_clicked(self):
|
||||
piece_decorative.PS_DXF16_jscode_fun('快速定位码链接();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
|
||||
def on_Kbtn4_2_clicked(self):
|
||||
piece_decorative.PS_DXF17_jscode_fun('定位码批量化替换外链新();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
def on_Dbtn4_1_clicked(self):
|
||||
piece_decorative.PS_DXF8_jscode_fun('图像分割();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
def on_Dbtn4_2_clicked(self):
|
||||
piece_decorative.PS_DXF15_jscode_fun('图层自动编组2();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
def on_Dbtn4_3_clicked(self):
|
||||
piece_decorative.PS_DXF15_jscode_fun('快速超级链接2();')
|
||||
# piece_decorative.PS_DXF22_jscode_fun('模特换衣功能();')
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
def on_Pbtn4_4_clicked(self):
|
||||
piece_decorative.PS_DXF26_jscode_fun('模特换图();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
def on_Pbtn4_5_clicked(self):
|
||||
piece_decorative.PS_DXF23_jscode_fun('自动连晒();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
def on_Pbtn4_6_clicked(self):
|
||||
piece_decorative.PS_DXF24_jscode_fun('自动米样拼贴();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
|
||||
def on_Pbtn4_1_clicked(self):
|
||||
piece_decorative.PS_DXF8_jscode_fun('码标添加2();')
|
||||
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
def on_Pbtn4_2_clicked(self):
|
||||
piece_decorative.PS_DXF8_jscode_fun('批量分辨率修改();')
|
||||
print("按钮被点击")
|
||||
pass
|
||||
|
||||
|
||||
def on_Pbtn4_3_clicked(self):
|
||||
piece_decorative.PS_DXF8_jscode_fun('批量款号添加();')
|
||||
print("按钮被点击")
|
||||
pass
|
||||
# 槽函数示例
|
||||
def on_standardize_pattern_clicked(self):
|
||||
piece_decorative.PS_DXF5_jscode_fun('花样标准化3();')
|
||||
print("花样标准化按钮被点击")
|
||||
|
||||
|
||||
def on_pattern_to_external_clicked(self):
|
||||
piece_decorative.PS_DXF5_jscode_fun('花样图层导出();')
|
||||
print("花样转外链按钮被点击")
|
||||
|
||||
def on_quick_change_clicked(self):
|
||||
piece_decorative.PS_DXF5_jscode_fun('替换外链新();')
|
||||
print("快速换图按钮被点击")
|
||||
|
||||
|
||||
|
||||
|
||||
def on_batch_quick_change_clicked(self):
|
||||
piece_decorative.PS_DXF8_jscode_fun('批量化替换外链新();')
|
||||
print("批量快速换图按钮被点击")
|
||||
|
||||
def on_btn4_1_clicked(self):
|
||||
# 处理新盒子1中按钮4_1的点击事件
|
||||
piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
piece_decorative.PS_DXF5_jscode_fun('裁片射出模板();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
pass
|
||||
|
||||
def on_btn4_2_clicked(self):
|
||||
# 处理新盒子1中按钮4_2的点击事件
|
||||
piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
piece_decorative.PS_DXF4_jscode_fun('裁片射出宽高缩放模板();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
pass
|
||||
|
||||
def on_btn4_3_clicked(self):
|
||||
# 处理新盒子1中按钮4_3的点击事件
|
||||
piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
piece_decorative.PS_DXF14_jscode_fun('裁片射出宽高缩放模板按中心();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
pass
|
||||
|
||||
def on_btn4_4_clicked(self):
|
||||
# 处理新盒子1中按钮4_3的点击事件
|
||||
piece_decorative.PS_DXF_jscode_fun('删除指定名称蒙版();')
|
||||
piece_decorative.PS_DXF7_jscode_fun('裁片射出缩放模板();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('信息激活2();')
|
||||
pass
|
||||
|
||||
|
||||
# def on_Dbtn4_1_clicked(self):
|
||||
# # 处理新盒子1中按钮4_4的点击事件
|
||||
# piece_decorative.PS_DXF8_jscode_fun('图像分割();')
|
||||
# pass
|
||||
|
||||
# def on_Pbtn4_5_clicked(self):
|
||||
# # 处理新盒子1中按钮4_4的点击事件
|
||||
# piece_decorative.PS_DXF11_jscode_fun('批量重设画布幅宽 ();')
|
||||
# pass
|
||||
# def on_Pbtn4_6_clicked(self):
|
||||
# # 处理新盒子1中按钮4_4的点击事件
|
||||
# piece_decorative.PS_DXF11_jscode_fun('批量重设画布幅宽();')
|
||||
# pass
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
dialog = ImportPDFDialog2()
|
||||
dialog.show()
|
||||
sys.exit(app.exec_())
|
||||
81
temp_backup/Server_redundant/tempdemo/psmark/Tab4.py
Normal file
@@ -0,0 +1,81 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout, QPushButton, QGroupBox, QLabel, QFrame
|
||||
from PyQt5 import QtGui
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget
|
||||
from PyQt5.QtGui import QPixmap
|
||||
import os
|
||||
class ImportPDFDialog4(QMainWindow):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle('Import Multiple PDF pages')
|
||||
|
||||
main_widget = QWidget()
|
||||
self.setCentralWidget(main_widget)
|
||||
main_layout = QVBoxLayout()
|
||||
main_widget.setLayout(main_layout)
|
||||
|
||||
# 快速换图
|
||||
quick_change_group = QGroupBox('版本介绍')
|
||||
quick_change_layout = QVBoxLayout(quick_change_group)
|
||||
|
||||
# 创建图像标签1并调整大小
|
||||
image_label1 = QLabel()
|
||||
# 获取当前脚本所在目录的绝对路径
|
||||
current_directory = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# 构建相对路径
|
||||
image_filename = "img/微信图片_20230906013631.jpg"
|
||||
relative_path = os.path.join(current_directory, image_filename)
|
||||
|
||||
# 创建 QPixmap 对象
|
||||
pixmap1 = QtGui.QPixmap(relative_path)
|
||||
pixmap1 = pixmap1.scaledToWidth(200) # 设置宽度限制为200像素
|
||||
image_label1.setPixmap(pixmap1)
|
||||
quick_change_layout.addWidget(image_label1)
|
||||
|
||||
# 创建文本标签
|
||||
text_label1 = QLabel('关注抖音查看视频教程')
|
||||
text_label1.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
quick_change_layout.addWidget(text_label1)
|
||||
|
||||
# 创建分隔线
|
||||
divider1 = QFrame()
|
||||
divider1.setFrameShape(QFrame.HLine)
|
||||
quick_change_layout.addWidget(divider1)
|
||||
|
||||
# 创建图像标签2并调整大小
|
||||
image_label2 = QLabel()
|
||||
image_filename2 = "img/微信图片_20230906013548.jpg"
|
||||
relative_path2 = os.path.join(current_directory, image_filename2)
|
||||
|
||||
# 创建另一个 QPixmap 对象,使用不同的变量名
|
||||
pixmap2 = QtGui.QPixmap(relative_path2)
|
||||
pixmap2 = pixmap2.scaledToWidth(200) # 设置宽度限制为200像素
|
||||
image_label2.setPixmap(pixmap2)
|
||||
quick_change_layout.addWidget(image_label2)
|
||||
|
||||
# 创建文本标签
|
||||
text_label2 = QLabel('BUG提交 功能开发 使用反馈 请联系微信')
|
||||
text_label2.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
quick_change_layout.addWidget(text_label2)
|
||||
|
||||
# 创建分隔线
|
||||
divider2 = QFrame()
|
||||
divider2.setFrameShape(QFrame.HLine)
|
||||
quick_change_layout.addWidget(divider2)
|
||||
|
||||
# 创建文本标签
|
||||
text_label3 = QLabel('PS Mark 版本号1.8(2023.10.4) by:jimi')
|
||||
text_label3.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
quick_change_layout.addWidget(text_label3)
|
||||
|
||||
quick_change_group.setLayout(quick_change_layout)
|
||||
main_layout.addWidget(quick_change_group)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
dialog = ImportPDFDialog4()
|
||||
dialog.show()
|
||||
sys.exit(app.exec_())
|
||||
449
temp_backup/Server_redundant/tempdemo/psmark/Tab5.py
Normal file
@@ -0,0 +1,449 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QLabel, QPushButton, QFileDialog, \
|
||||
QLineEdit, QScrollArea, QGroupBox, QHBoxLayout, QMessageBox, QProgressDialog
|
||||
from PyQt5.QtGui import QIntValidator
|
||||
from PyQt5.QtCore import QTimer
|
||||
import re
|
||||
import os
|
||||
import ezdxf
|
||||
import ezdxf.tools
|
||||
import ezdxf.bbox
|
||||
import ezdxf.units
|
||||
import ezdxf.math
|
||||
from coreldraw_checker import is_coreldraw_running
|
||||
from functools import partial
|
||||
import win32com.client
|
||||
import os
|
||||
import shutil
|
||||
import threading
|
||||
from clear_folder import another_function
|
||||
|
||||
|
||||
class YourMainWindow5(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.setWindowTitle("多码混排")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
self.button1 = QPushButton("DXF文件路径")
|
||||
self.button1.setFixedWidth(200)
|
||||
self.button1.clicked.connect(self.showDxfFileDialog)
|
||||
|
||||
self.button2 = QPushButton("PLT文件路径")
|
||||
self.button2.setFixedWidth(200)
|
||||
self.button2.clicked.connect(self.showPltFileDialog)
|
||||
|
||||
self.initial_button1_text = self.button1.text()
|
||||
self.initial_button2_text = self.button2.text()
|
||||
|
||||
panel1 = QGroupBox("文件路径")
|
||||
panel1_layout = QVBoxLayout(panel1)
|
||||
panel1_layout.setSpacing(10)
|
||||
panel1_layout.setContentsMargins(10, 10, 10, 10)
|
||||
panel1_layout.addWidget(self.button1)
|
||||
panel1_layout.addWidget(self.button2)
|
||||
layout.addWidget(panel1)
|
||||
|
||||
self.label5 = QLabel('单码片数')
|
||||
self.lineEdit3 = QLineEdit()
|
||||
self.lineEdit3.setValidator(QIntValidator())
|
||||
self.lineEdit3.setFixedSize(120, 30)
|
||||
layout.addWidget(self.label5)
|
||||
layout.addWidget(self.lineEdit3)
|
||||
|
||||
self.scrollWidget = QWidget()
|
||||
self.scrollWidgetLayout = QVBoxLayout(self.scrollWidget)
|
||||
self.scrollArea = QScrollArea()
|
||||
self.scrollArea.setWidget(self.scrollWidget)
|
||||
self.scrollArea.setWidgetResizable(True)
|
||||
layout.addWidget(self.scrollArea)
|
||||
|
||||
self.clearButton = QPushButton("清空信息")
|
||||
self.clearButton.clicked.connect(self.clearScrollArea)
|
||||
layout.addWidget(self.clearButton)
|
||||
|
||||
confirm_button = QPushButton("分割")
|
||||
confirm_button.clicked.connect(self.updateScrollArea)
|
||||
#confirm_button.clicked.connect(self.freezeAndParse) # 连接按钮点击事件
|
||||
layout.addWidget(confirm_button)
|
||||
|
||||
|
||||
central_widget = QWidget()
|
||||
central_widget.setLayout(layout)
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
self.dxfLineEdits = {}
|
||||
|
||||
self.allowButtonActions = True # 标志变量,控制是否允许按钮行为
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def run_coreldraw_macros(self):
|
||||
try:
|
||||
dogms = win32com.client.DispatchEx("CorelDRAW.Application.23")
|
||||
|
||||
macros_to_run = [
|
||||
"RUN.OpenDXFFilesInFolder",
|
||||
"RUN.RotateSelectionClockwise",
|
||||
"RUN.DeleteUnnamedSublayers",
|
||||
"RUN.StToFront",
|
||||
"RUN.IterateSublayerNames",
|
||||
|
||||
|
||||
]
|
||||
|
||||
for macro in macros_to_run:
|
||||
dogms.GMSManager.RunMacro("RUNDXF", macro)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "警告", "缺少CDR模块,请载入CDR模块", QMessageBox.Ok)
|
||||
# print("缺少CDR模块,请载入CDR模块")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def showPltFileDialog(self):
|
||||
options = QFileDialog.Options()
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, "选择PLT文件", "", "PLT Files (*.plt);;All Files (*)",
|
||||
options=options)
|
||||
if file_path:
|
||||
print("Selected PLT file:", file_path)
|
||||
extracted_content = self.extract_content_from_plt_path(file_path)
|
||||
print(extracted_content)
|
||||
self.sizes = self.fill_sizes_from_extracted_content(extracted_content)
|
||||
|
||||
# 更新尺寸字典后,清空并填充滚动区域
|
||||
self.clearScrollArea()
|
||||
self.populateScrollArea()
|
||||
|
||||
self.initial_plt_path = file_path # 更新初始路径而不更新按钮文本
|
||||
|
||||
else:
|
||||
print("No PLT file selected")
|
||||
QMessageBox.warning(self, "警告", "没有选择文件夹。请重新选择文件夹。", QMessageBox.Ok)
|
||||
|
||||
pass
|
||||
|
||||
def showDxfFileDialog(self):
|
||||
options = QFileDialog.Options()
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, "选择DXF文件", "", "DXF Files (*.dxf);;All Files (*)",
|
||||
options=options)
|
||||
if file_path:
|
||||
print("Selected DXF file:", file_path)
|
||||
self.initial_dxf_path = file_path # 更新初始路径而不更新按钮文本
|
||||
else:
|
||||
print("No DXF file selected")
|
||||
QMessageBox.warning(self, "警告", "没有选择文件夹。请重新选择文件夹。", QMessageBox.Ok)
|
||||
# 在此处添加提醒逻辑,例如使用 QMessageBox 提示用户没有选择文件
|
||||
|
||||
def extract_content_from_plt_path(self, plt_path):
|
||||
match = re.search(r'\((.*?)\)', plt_path)
|
||||
if match:
|
||||
extracted_content = match.group(1)
|
||||
return extracted_content
|
||||
else:
|
||||
return "No content in parentheses found"
|
||||
|
||||
def fill_sizes_from_extracted_content(self, extracted_content):
|
||||
sizes = extracted_content.split("+")
|
||||
size_dict = {}
|
||||
for size in sizes:
|
||||
size_dict[size] = ""
|
||||
return size_dict
|
||||
|
||||
def process_dxf_file(self, file_path, extracted_content):
|
||||
doc = ezdxf.readfile(file_path)
|
||||
msp = doc.modelspace()
|
||||
mspBox = ezdxf.bbox.extents(msp)
|
||||
|
||||
print("=====", os.path.basename(file_path))
|
||||
print("左上角坐标:", mspBox.extmin)
|
||||
print("画布宽:", mspBox.size[0], "画布高:", mspBox.size[1])
|
||||
print()
|
||||
|
||||
for entity in msp.query():
|
||||
if entity.dxftype() == "INSERT":
|
||||
temp = []
|
||||
rotation = None
|
||||
block = doc.blocks[entity.dxf.name]
|
||||
|
||||
for e in block:
|
||||
if e.dxftype() != "TEXT":
|
||||
temp.append(e)
|
||||
else:
|
||||
rotation = e.dxf.rotation
|
||||
|
||||
if rotation is not None:
|
||||
rotation %= 360
|
||||
if 45 <= rotation < 135:
|
||||
rotation = 90
|
||||
elif 135 <= rotation < 225:
|
||||
rotation = 180
|
||||
elif 225 <= rotation < 315:
|
||||
rotation = -90
|
||||
else:
|
||||
rotation = 0
|
||||
|
||||
print("=====", entity.dxf.name)
|
||||
print("大小:", ezdxf.bbox.extents(temp).size)
|
||||
print("文字角度:", rotation)
|
||||
|
||||
center = ezdxf.bbox.extents(temp).center
|
||||
center = (center.x, mspBox.extmax.y - center.y) # 调整center y值
|
||||
|
||||
print("中心坐标:", center)
|
||||
|
||||
center = (center[1], mspBox.size[0] - center[0]) # 旋转后中心坐标
|
||||
print("旋转后中心坐标:", center)
|
||||
|
||||
separator = "_" # 分隔符
|
||||
entity.dxf.name += separator + str(rotation)
|
||||
block.name += separator + str(rotation)
|
||||
|
||||
new_file_path = os.path.join(r"D:\marktemp", "{}.dxf".format(extracted_content))
|
||||
doc.saveas(new_file_path)
|
||||
def getSinglePieceCount(self):
|
||||
return self.lineEdit3.text()
|
||||
|
||||
def recreate_folders(self):
|
||||
# 定义文件夹路径
|
||||
folder_paths = [r"D:\PSMARKtemp", r"D:\marktemp"]
|
||||
|
||||
# 删除文件夹及其内容
|
||||
for folder_path in folder_paths:
|
||||
if os.path.exists(folder_path):
|
||||
shutil.rmtree(folder_path)
|
||||
print(f"Deleted folder: {folder_path}")
|
||||
|
||||
# 重新创建文件夹
|
||||
for folder_path in folder_paths:
|
||||
os.makedirs(folder_path)
|
||||
print(f"Recreated folder: {folder_path}")
|
||||
|
||||
def freezeAndParse(self):
|
||||
self.parse_button.setEnabled(False) # 冻结按钮
|
||||
QTimer.singleShot(10000, self.unfreezeButton) # 10秒后解冻按钮
|
||||
|
||||
def unfreezeButton(self):
|
||||
self.parse_button.setEnabled(True) # 解冻按钮
|
||||
|
||||
|
||||
def updateScrollArea(self):
|
||||
|
||||
another_function()
|
||||
|
||||
if not is_coreldraw_running():
|
||||
QMessageBox.warning(self, "警告", "CorelDRAW未运行,无法执行操作。")
|
||||
return
|
||||
|
||||
plt_file_path = self.initial_plt_path
|
||||
|
||||
# 获取DXF文件路径
|
||||
dxf_file_path = self.initial_dxf_path
|
||||
extracted_content = self.extract_content_from_plt_path(plt_file_path)
|
||||
|
||||
if dxf_file_path:
|
||||
# 去掉括号内内容后的PLT文件名作为DXF文件名
|
||||
plt_filename = os.path.basename(plt_file_path)
|
||||
# dxf_filename = re.sub(r'\(.*?\)', '', plt_filename)
|
||||
self.process_dxf_file(dxf_file_path, extracted_content) # 调用解析函数并传入单码片数和新的DXF文件名
|
||||
print("DXF文件解析完成!")
|
||||
else:
|
||||
QMessageBox.warning(self, "警告", "没有选择DXF文件。请先选择一个DXF文件。", QMessageBox.Ok)
|
||||
print()
|
||||
|
||||
|
||||
|
||||
self.run_coreldraw_macros()
|
||||
single_code_pieces = int(self.getSinglePieceCount()) # 获取单码片数
|
||||
print(single_code_pieces)
|
||||
# 打印滚动区域中的输入框内容
|
||||
code_quantities = {} # 创建一个新的字典用于存储数据
|
||||
|
||||
for label, line_edit in self.dxfLineEdits.items():
|
||||
text = line_edit.text()
|
||||
# print(f"Label: {label}, Text: {text}")
|
||||
if text.isdigit():
|
||||
value = int(text) # 尝试将文本转换为整数
|
||||
else:
|
||||
try:
|
||||
value = float(text) # 尝试将文本转换为浮点数
|
||||
except ValueError:
|
||||
print(f"Invalid value for {label}: {text}")
|
||||
continue # 转换失败,跳过当前循环迭代
|
||||
|
||||
code_quantities[label] = value # 存储转换后的数字到字典
|
||||
# print(line_edit)
|
||||
print(code_quantities)
|
||||
|
||||
keys_list = list(code_quantities.keys())
|
||||
values_list = list(code_quantities.values())
|
||||
|
||||
# length = len(code_quantities)
|
||||
# print(length) # 输出 3,因为字典中有三对键值对
|
||||
|
||||
|
||||
# 将text中的每个元素与code对应位置的元素相乘
|
||||
result = [txt * single_code_pieces for txt in values_list]
|
||||
|
||||
# 使用zip函数将label和result对应组合
|
||||
combined = zip(keys_list, result)
|
||||
|
||||
# 利用列表推导式生成结果数组
|
||||
results2 = [lbl for lbl, txt in combined for _ in range(txt)]
|
||||
|
||||
# 打印结果数组
|
||||
print(results2)
|
||||
|
||||
###############debug
|
||||
corel_app = win32com.client.Dispatch("CorelDRAW.Application.23")
|
||||
|
||||
# 获取当前活动文档
|
||||
active_document = corel_app.ActiveDocument
|
||||
|
||||
# 获取当前页面中所有图层的名称,排除特定名称的图层
|
||||
layer_names = [layer.Name for layer in active_document.ActivePage.Layers
|
||||
if layer.Name not in ["辅助线", "1", "0", "Defpoints"]]
|
||||
|
||||
p_numbers = [] # 初始化存储 P 数字的列表
|
||||
|
||||
for code, quantity in code_quantities.items():
|
||||
for i in range(1, single_code_pieces + 1):
|
||||
p_numbers.extend([f"P{i}"] * int(quantity))
|
||||
|
||||
print(p_numbers)
|
||||
|
||||
|
||||
|
||||
|
||||
# 循环遍历不同的码
|
||||
# p_numbers = [] # 初始化存储 P 数字的列表
|
||||
#
|
||||
# for code, quantity in code_quantities.items():
|
||||
# for i in range(1, single_code_pieces + 1):
|
||||
# # 根据码的数量分别生成对应数量的 P 数字,并添加到列表中
|
||||
# p_numbers.extend([f"P{i}"] * quantity)
|
||||
# print(p_numbers)
|
||||
new_layer_names = []
|
||||
index = 0
|
||||
for old_name in layer_names:
|
||||
parts = old_name.split("-") # 根据"-"分割字符串
|
||||
if len(parts) > 1:
|
||||
new_name = f"{p_numbers[index]}-{parts[1]}-{results2[index]}" # 使用数组中的 P 数字
|
||||
new_layer_names.append(new_name)
|
||||
index += 1
|
||||
|
||||
# 在新的图层名数组中遍历,对图层进行修改
|
||||
modified_names = [] # 创建一个列表来存储修改后的名称
|
||||
modified_names2 = []
|
||||
for i, new_name in enumerate(new_layer_names):
|
||||
active_document.ActivePage.Layers(layer_names[i]).Name = new_name
|
||||
modified_names2.append(new_name)
|
||||
modified_names.append(new_name)
|
||||
print(f"Modified: {layer_names[i]} -> {new_name}")
|
||||
|
||||
dogms = win32com.client.DispatchEx("CorelDRAW.Application.23")
|
||||
|
||||
#
|
||||
dogms.GMSManager.RunMacro("RUNDXF", "RUN.RUNPDF")
|
||||
#
|
||||
dogms.GMSManager.RunMacro("RUNDXF", "RUN.ActiveDocumentClose")
|
||||
###############debug
|
||||
|
||||
# print(modified_names)
|
||||
|
||||
######### ######### ######### ######### #########这里是对多码的功能 这里是删除的功能
|
||||
# def delete_layers_by_names(names_to_delete, active_document):
|
||||
# corel_app = win32com.client.Dispatch("CorelDRAW.Application.23")
|
||||
#
|
||||
# # 获取当前活动文档
|
||||
# active_document = corel_app.ActiveDocument
|
||||
# for target_layer_name in names_to_delete:
|
||||
# for layer in active_document.ActivePage.Layers:
|
||||
# if layer.Name == target_layer_name:
|
||||
# layer.Delete()
|
||||
# break # 找到目标图层后中断循环
|
||||
#
|
||||
#
|
||||
#
|
||||
# modified_names_list = [] # 用于存储每次循环中的 modified_names 列表
|
||||
# ######### ######### ######### ######### #########这里是对多码的功能 这里是导出PSD的功能 我这边要混排就对功能进行封禁
|
||||
# Index = 0
|
||||
# for code in code_quantities:
|
||||
# quantity = code_quantities[code]
|
||||
# total_pieces = quantity * single_code_pieces
|
||||
#
|
||||
# # 获取数组的前 total_pieces 个元素
|
||||
# newmodified_names_filtered = modified_names[:total_pieces]
|
||||
# # print(newmodified_names_filtered)
|
||||
#
|
||||
# result_array = [fruit for fruit in modified_names2 if fruit not in newmodified_names_filtered]
|
||||
# result_array_length = len(result_array)
|
||||
# print(result_array_length)
|
||||
#
|
||||
# delete_layers_by_names(result_array, active_document.ActivePage)
|
||||
# # modified_names_list.append(modified_names) # 将 modified_names 添加到数组中
|
||||
#
|
||||
#
|
||||
# dogms = win32com.client.DispatchEx("CorelDRAW.Application.23")
|
||||
#
|
||||
# dogms.GMSManager.RunMacro("RUNDXF", "RUN.ExportSelectionToPSD", Index)
|
||||
#
|
||||
# dogms.GMSManager.RunMacro("RUNDXF", "RUN.HOURUN", result_array_length)
|
||||
#
|
||||
# modified_names = modified_names[total_pieces:]
|
||||
#
|
||||
# Index += 1
|
||||
#
|
||||
#
|
||||
# corel_app = win32com.client.Dispatch("CorelDRAW.Application.23")
|
||||
#
|
||||
# corel_app .GMSManager.RunMacro("RUNDXF", "RUN.ActiveDocumentClose")
|
||||
#
|
||||
# QMessageBox.warning(self, "提醒", "分割完成,请进行裁片套版操作。")
|
||||
|
||||
def updateLineEditsFromSizes(self):
|
||||
for size_label, line_edit in self.dxfLineEdits.items():
|
||||
self.sizes[size_label] = line_edit.text()
|
||||
|
||||
def populateScrollArea(self):
|
||||
self.clearScrollArea()
|
||||
for size_label, size_text in self.sizes.items():
|
||||
size_layout = QHBoxLayout()
|
||||
size_layout.addWidget(QLabel(size_label))
|
||||
|
||||
line_edit = QLineEdit()
|
||||
line_edit.setValidator(QIntValidator())
|
||||
line_edit.setFixedSize(100, 30)
|
||||
line_edit.setText(size_text)
|
||||
|
||||
self.dxfLineEdits[size_label] = line_edit
|
||||
size_layout.addWidget(line_edit)
|
||||
|
||||
self.scrollWidgetLayout.addLayout(size_layout)
|
||||
|
||||
def clearScrollArea(self):
|
||||
for i in reversed(range(self.scrollWidgetLayout.count())):
|
||||
item = self.scrollWidgetLayout.itemAt(i)
|
||||
if isinstance(item, QHBoxLayout) or isinstance(item, QVBoxLayout):
|
||||
while item.count():
|
||||
widget = item.takeAt(0).widget()
|
||||
if widget:
|
||||
widget.deleteLater()
|
||||
self.dxfLineEdits.clear() # 清空部件引用
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
mainWindow = YourMainWindow5()
|
||||
mainWindow.show()
|
||||
sys.exit(app.exec_())
|
||||
226
temp_backup/Server_redundant/tempdemo/psmark/Tab6.py
Normal file
@@ -0,0 +1,226 @@
|
||||
import json
|
||||
import os
|
||||
#import replicate
|
||||
import requests
|
||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||
|
||||
# from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
# load_dotenv() # 加载 .env 文件中的环境变量
|
||||
url = 'http://43.139.183.222:5000'
|
||||
|
||||
|
||||
|
||||
|
||||
class ImportPDFDialog6(QtWidgets.QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.initUI()
|
||||
self.processed_image_path = None # 存储处理后的图片路径
|
||||
|
||||
def initUI(self):
|
||||
self.setWindowTitle('图像处理参数输入')
|
||||
|
||||
# 布局
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
|
||||
# 文件选择行
|
||||
self.file_input = QtWidgets.QLineEdit(self)
|
||||
# self.file_input.setPlaceholderText("选择文件路径 (如 1.jpg)")
|
||||
layout.addWidget(self.file_input)
|
||||
|
||||
self.browse_button = QtWidgets.QPushButton('浏览', self)
|
||||
self.browse_button.clicked.connect(self.browse_file)
|
||||
layout.addWidget(self.browse_button)
|
||||
|
||||
self.prompt_input = QtWidgets.QLineEdit(self)
|
||||
self.prompt_input.setPlaceholderText("提示词 ")
|
||||
layout.addWidget(self.prompt_input)
|
||||
|
||||
self.creativity_input = QtWidgets.QDoubleSpinBox(self)
|
||||
self.creativity_input.setRange(0, 1)
|
||||
self.creativity_input.setSingleStep(0.01)
|
||||
self.creativity_input.setValue(0.35) # 默认值
|
||||
layout.addWidget(QtWidgets.QLabel("创造力 (0 - 3):"))
|
||||
layout.addWidget(self.creativity_input)
|
||||
|
||||
self.resemblance_input = QtWidgets.QDoubleSpinBox(self)
|
||||
self.resemblance_input.setRange(0, 3)
|
||||
self.resemblance_input.setSingleStep(0.01)
|
||||
self.resemblance_input.setValue(0.6) # 默认值
|
||||
layout.addWidget(QtWidgets.QLabel("相似度 (0 - 3):"))
|
||||
layout.addWidget(self.resemblance_input)
|
||||
|
||||
self.scale_factor_input = QtWidgets.QSpinBox(self)
|
||||
self.scale_factor_input.setRange(2, 8)
|
||||
self.scale_factor_input.setValue(2) # 默认值
|
||||
layout.addWidget(QtWidgets.QLabel("放大倍数:"))
|
||||
layout.addWidget(self.scale_factor_input)
|
||||
|
||||
self.submit_button = QtWidgets.QPushButton('提交', self)
|
||||
self.submit_button.clicked.connect(self.submit)
|
||||
layout.addWidget(self.submit_button)
|
||||
|
||||
# 合并图片显示和处理结果区域
|
||||
self.output_area = QtWidgets.QVBoxLayout()
|
||||
self.image_label = QtWidgets.QLabel(self)
|
||||
# self.image_label.setText("加载的图片将显示在这里")
|
||||
self.image_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.output_area.addWidget(self.image_label)
|
||||
|
||||
self.result_display = QtWidgets.QTextEdit(self)
|
||||
self.result_display.setReadOnly(True)
|
||||
self.output_area.addWidget(QtWidgets.QLabel("处理结果:"))
|
||||
self.output_area.addWidget(self.result_display)
|
||||
|
||||
layout.addLayout(self.output_area)
|
||||
|
||||
# 下载按钮
|
||||
self.download_button = QtWidgets.QPushButton('下载', self)
|
||||
self.download_button.clicked.connect(self.download_image)
|
||||
self.download_button.setEnabled(False) # 初始不可用
|
||||
layout.addWidget(self.download_button)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def browse_file(self):
|
||||
options = QtWidgets.QFileDialog.Options()
|
||||
file_name, _ = QtWidgets.QFileDialog.getOpenFileName(self, "选择图像文件", "", "Image Files (*.png *.jpg *.jpeg *.bmp)", options=options)
|
||||
if file_name:
|
||||
self.file_input.setText(file_name)
|
||||
self.display_image(file_name) # 显示选中的图片
|
||||
|
||||
def display_image(self, file_path):
|
||||
pixmap = QtGui.QPixmap(file_path)
|
||||
self.image_label.setPixmap(pixmap.scaled(300, 300, QtCore.Qt.KeepAspectRatio))
|
||||
|
||||
def upload_image(self, url, file_path, json_data):
|
||||
imgurl = url + "/upload"
|
||||
with open(file_path, 'rb') as file:
|
||||
files = {'file': file}
|
||||
data = {'data': json.dumps(json_data)} # 将 JSON 数据转换为字符串
|
||||
response = requests.post(imgurl, files=files, data=data)
|
||||
|
||||
print(response.status_code)
|
||||
response_json = response.json() # 先解析 JSON 响应
|
||||
|
||||
print(response_json) # 打印响应内容以检查其格式
|
||||
|
||||
# 确保响应是一个列表,并且至少有一个元素
|
||||
if isinstance(response_json, list) and len(response_json) > 0:
|
||||
self.processed_image_path = response_json[0] # 从列表中获取 URL
|
||||
else:
|
||||
# 处理不符合预期的情况
|
||||
print("Unexpected response format:", response_json)
|
||||
return
|
||||
|
||||
self.result_display.setPlainText(str(response_json)) # 显示处理结果
|
||||
self.download_button.setEnabled(True) # 启用下载按钮
|
||||
self.display_processed_image() # 显示处理后的图片
|
||||
|
||||
def submit(self):
|
||||
try:
|
||||
creativity = self.creativity_input.value()
|
||||
file_path = self.file_input.text()
|
||||
urlprompt = self.prompt_input.text() + " best quality, highres, <lora:more_details:0.5> <lora:SDXLrender_v2.0:1>"
|
||||
prompt = urlprompt
|
||||
resemblance = self.resemblance_input.value()
|
||||
scale_factor = self.scale_factor_input.value()
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
raise ValueError("文件不存在,请选择有效的文件。")
|
||||
|
||||
input_data = {
|
||||
"seed": 1337,
|
||||
"prompt": prompt,
|
||||
"dynamic": 6,
|
||||
"handfix": "disabled",
|
||||
"pattern": False,
|
||||
"sharpen": 0,
|
||||
"sd_model": "juggernaut_reborn.safetensors [338b85bc4f]",
|
||||
"scheduler": "DPM++ 3M SDE Karras",
|
||||
"creativity": creativity,
|
||||
"lora_links": "",
|
||||
"downscaling": False,
|
||||
"resemblance": resemblance,
|
||||
"scale_factor": scale_factor,
|
||||
"tiling_width": 112,
|
||||
"tiling_height": 144,
|
||||
"output_format": "png",
|
||||
"custom_sd_model": "",
|
||||
"negative_prompt": "(worst quality, low quality, normal quality:2) JuggernautNegative-neg",
|
||||
"num_inference_steps": 18,
|
||||
"downscaling_resolution": 768
|
||||
}
|
||||
|
||||
# 确保调用时只传递三个参数
|
||||
self.upload_image(url, file_path, input_data)
|
||||
|
||||
except Exception as e:
|
||||
QtWidgets.QMessageBox.critical(self, "错误", str(e))
|
||||
|
||||
|
||||
def run(url):
|
||||
|
||||
newurl = url + "/data"
|
||||
data = {'key': 'value'} # 你要发送的数据
|
||||
response = requests.post(newurl, json=data)
|
||||
|
||||
print(response.status_code) # 打印状态码
|
||||
print(response.json()) # 打印返回的 JSON 数据
|
||||
|
||||
def run2(url):
|
||||
|
||||
newurl = url + "/dataimg"
|
||||
data = {'key': 'value'} # 你要发送的数据
|
||||
response = requests.post(newurl, json=data)
|
||||
|
||||
print(response.status_code) # 打印状态码
|
||||
print(response.json()) # 打印返回的 JSON 数据
|
||||
|
||||
|
||||
# if __name__ == '__main__':
|
||||
# upload_image(url, '1.jpg', {'key': 'value'})
|
||||
|
||||
|
||||
|
||||
def display_processed_image(self):
|
||||
# 下载并显示处理后的图片
|
||||
response = requests.get(self.processed_image_path)
|
||||
if response.status_code == 200:
|
||||
pixmap = QtGui.QPixmap()
|
||||
pixmap.loadFromData(response.content)
|
||||
self.image_label.setPixmap(pixmap.scaled(300, 300, QtCore.Qt.KeepAspectRatio))
|
||||
|
||||
def download_image(self):
|
||||
if self.processed_image_path:
|
||||
image_url = self.processed_image_path
|
||||
|
||||
file_name, _ = QtWidgets.QFileDialog.getSaveFileName(self, "保存处理后的图片", "", "Image Files (*.png *.jpg *.jpeg)")
|
||||
if file_name:
|
||||
try:
|
||||
response = requests.get(image_url)
|
||||
response.raise_for_status() # 检查请求是否成功
|
||||
with open(file_name, 'wb') as f:
|
||||
f.write(response.content)
|
||||
QtWidgets.QMessageBox.information(self, "成功", "图片下载完成!")
|
||||
except requests.exceptions.RequestException as e:
|
||||
QtWidgets.QMessageBox.critical(self, "错误", f"下载失败:{e}")
|
||||
except Exception as e:
|
||||
QtWidgets.QMessageBox.critical(self, "错误", f"文件保存失败:{e}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
# 创建并显示登录界面
|
||||
# login_dialog = LoginDialog()
|
||||
# if login_dialog.exec_() == QtWidgets.QDialog.Accepted:
|
||||
# 只有在登录成功后才执行以下代码
|
||||
ex = ImportPDFDialog6()
|
||||
ex.show()
|
||||
|
||||
sys.exit(app.exec_())
|
||||
33
temp_backup/Server_redundant/tempdemo/psmark/clear_folder.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
import threading
|
||||
|
||||
def clear_folder_contents(folder_paths):
|
||||
def clear_folder(folder_path):
|
||||
try:
|
||||
for filename in os.listdir(folder_path):
|
||||
file_path = os.path.join(folder_path, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path):
|
||||
os.unlink(file_path)
|
||||
elif os.path.isdir(file_path):
|
||||
clear_folder(file_path)
|
||||
os.rmdir(file_path)
|
||||
except Exception as e:
|
||||
print(f"无法删除 {file_path}: {e}")
|
||||
except Exception as e:
|
||||
print(f"无法列出文件夹内容 {folder_path}: {e}")
|
||||
|
||||
for folder_path in folder_paths:
|
||||
thread = threading.Thread(target=clear_folder, args=(folder_path,))
|
||||
thread.start()
|
||||
|
||||
def another_function():
|
||||
folder1_to_clear = "D:\PSMarktemp"
|
||||
#folder2_to_clear = "D:\MarkTemp\DXFmarktemp"
|
||||
folder3_to_clear = "D:\markTemp"
|
||||
folders_to_clear = [folder1_to_clear,folder3_to_clear]
|
||||
clear_folder_contents(folders_to_clear)
|
||||
|
||||
# 在另一个函数中执行清空两个文件夹内容的操作
|
||||
|
||||
# folder_paths = [r"D:\MarkTemp\PSMarktemp", r"D:\MarkTemp\marktemp", r"D:\MarkTemp\marktemp"]
|
||||
@@ -0,0 +1,7 @@
|
||||
import psutil
|
||||
|
||||
def is_coreldraw_running():
|
||||
for process in psutil.process_iter(['pid', 'name']):
|
||||
if process.info['name'] == "CorelDRW.exe":
|
||||
return True
|
||||
return False
|
||||
31
temp_backup/Server_redundant/tempdemo/psmark/cs.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
import threading
|
||||
|
||||
def clear_folder_contents(folder_paths):
|
||||
def clear_folder(folder_path):
|
||||
try:
|
||||
for filename in os.listdir(folder_path):
|
||||
file_path = os.path.join(folder_path, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path):
|
||||
os.unlink(file_path)
|
||||
elif os.path.isdir(file_path):
|
||||
clear_folder(file_path)
|
||||
os.rmdir(file_path)
|
||||
except Exception as e:
|
||||
print(f"无法删除 {file_path}: {e}")
|
||||
except Exception as e:
|
||||
print(f"无法列出文件夹内容 {folder_path}: {e}")
|
||||
|
||||
for folder_path in folder_paths:
|
||||
thread = threading.Thread(target=clear_folder, args=(folder_path,))
|
||||
thread.start()
|
||||
|
||||
def another_function():
|
||||
folder1_to_clear = "D:\PSMARKtemp"
|
||||
folder2_to_clear = "D:\marktemp"
|
||||
folders_to_clear = [folder1_to_clear, folder2_to_clear]
|
||||
clear_folder_contents(folders_to_clear)
|
||||
|
||||
# 在另一个函数中执行清空两个文件夹内容的操作
|
||||
|
||||
BIN
temp_backup/Server_redundant/tempdemo/psmark/icons/newapp.ico
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 114 KiB |
51
temp_backup/Server_redundant/tempdemo/psmark/main.spec
Normal file
@@ -0,0 +1,51 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['main.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='main',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['newapp.ico'],
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='main',
|
||||
)
|
||||
@@ -0,0 +1,86 @@
|
||||
from os.path import join, dirname, abspath
|
||||
from qtpy.QtGui import QPalette, QColor
|
||||
import qtpy, platform
|
||||
|
||||
QT_VERSION = tuple((int(v) for v in qtpy.QT_VERSION.split('.')))
|
||||
PLATFORM = platform.system()
|
||||
_STYLESHEET = 'black.css'
|
||||
|
||||
def _apply_base_theme(app):
|
||||
""" Apply base theme to the application.
|
||||
|
||||
Args:
|
||||
app (QApplication): QApplication instance.
|
||||
"""
|
||||
if QT_VERSION < (5, ):
|
||||
app.setStyle('plastique')
|
||||
else:
|
||||
app.setStyle('Fusion')
|
||||
with open(_STYLESHEET) as stylesheet:
|
||||
app.setStyleSheet(stylesheet.read())
|
||||
|
||||
|
||||
def dark(app):
|
||||
""" Apply Dark Theme to the Qt application instance.
|
||||
|
||||
Args:
|
||||
app (QApplication): QApplication instance.
|
||||
"""
|
||||
darkPalette = QPalette()
|
||||
darkPalette.setColor(QPalette.WindowText, QColor(180, 180, 180))
|
||||
darkPalette.setColor(QPalette.Button, QColor(53, 53, 53))
|
||||
darkPalette.setColor(QPalette.Light, QColor(180, 180, 180))
|
||||
darkPalette.setColor(QPalette.Midlight, QColor(90, 90, 90))
|
||||
darkPalette.setColor(QPalette.Dark, QColor(35, 35, 35))
|
||||
darkPalette.setColor(QPalette.Text, QColor(180, 180, 180))
|
||||
darkPalette.setColor(QPalette.BrightText, QColor(180, 180, 180))
|
||||
darkPalette.setColor(QPalette.ButtonText, QColor(180, 180, 180))
|
||||
darkPalette.setColor(QPalette.Base, QColor(42, 42, 42))
|
||||
darkPalette.setColor(QPalette.Window, QColor(53, 53, 53))
|
||||
darkPalette.setColor(QPalette.Shadow, QColor(20, 20, 20))
|
||||
darkPalette.setColor(QPalette.Highlight, QColor(42, 130, 218))
|
||||
darkPalette.setColor(QPalette.HighlightedText, QColor(180, 180, 180))
|
||||
darkPalette.setColor(QPalette.Link, QColor(56, 252, 196))
|
||||
darkPalette.setColor(QPalette.AlternateBase, QColor(66, 66, 66))
|
||||
darkPalette.setColor(QPalette.ToolTipBase, QColor(53, 53, 53))
|
||||
darkPalette.setColor(QPalette.ToolTipText, QColor(180, 180, 180))
|
||||
darkPalette.setColor(QPalette.Disabled, QPalette.WindowText, QColor(127, 127, 127))
|
||||
darkPalette.setColor(QPalette.Disabled, QPalette.Text, QColor(127, 127, 127))
|
||||
darkPalette.setColor(QPalette.Disabled, QPalette.ButtonText, QColor(127, 127, 127))
|
||||
darkPalette.setColor(QPalette.Disabled, QPalette.Highlight, QColor(80, 80, 80))
|
||||
darkPalette.setColor(QPalette.Disabled, QPalette.HighlightedText, QColor(127, 127, 127))
|
||||
app.setPalette(darkPalette)
|
||||
_apply_base_theme(app)
|
||||
|
||||
|
||||
def light(app):
|
||||
""" Apply Light Theme to the Qt application instance.
|
||||
|
||||
Args:
|
||||
app (QApplication): QApplication instance.
|
||||
"""
|
||||
lightPalette = QPalette()
|
||||
lightPalette.setColor(QPalette.WindowText, QColor(0, 0, 0))
|
||||
lightPalette.setColor(QPalette.Button, QColor(240, 240, 240))
|
||||
lightPalette.setColor(QPalette.Light, QColor(180, 180, 180))
|
||||
lightPalette.setColor(QPalette.Midlight, QColor(200, 200, 200))
|
||||
lightPalette.setColor(QPalette.Dark, QColor(225, 225, 225))
|
||||
lightPalette.setColor(QPalette.Text, QColor(0, 0, 0))
|
||||
lightPalette.setColor(QPalette.BrightText, QColor(0, 0, 0))
|
||||
lightPalette.setColor(QPalette.ButtonText, QColor(0, 0, 0))
|
||||
lightPalette.setColor(QPalette.Base, QColor(237, 237, 237))
|
||||
lightPalette.setColor(QPalette.Window, QColor(240, 240, 240))
|
||||
lightPalette.setColor(QPalette.Shadow, QColor(20, 20, 20))
|
||||
lightPalette.setColor(QPalette.Highlight, QColor(76, 163, 224))
|
||||
lightPalette.setColor(QPalette.HighlightedText, QColor(0, 0, 0))
|
||||
lightPalette.setColor(QPalette.Link, QColor(0, 162, 232))
|
||||
lightPalette.setColor(QPalette.AlternateBase, QColor(225, 225, 225))
|
||||
lightPalette.setColor(QPalette.ToolTipBase, QColor(240, 240, 240))
|
||||
lightPalette.setColor(QPalette.ToolTipText, QColor(0, 0, 0))
|
||||
lightPalette.setColor(QPalette.Disabled, QPalette.WindowText, QColor(115, 115, 115))
|
||||
lightPalette.setColor(QPalette.Disabled, QPalette.Text, QColor(115, 115, 115))
|
||||
lightPalette.setColor(QPalette.Disabled, QPalette.ButtonText, QColor(115, 115, 115))
|
||||
lightPalette.setColor(QPalette.Disabled, QPalette.Highlight, QColor(190, 190, 190))
|
||||
lightPalette.setColor(QPalette.Disabled, QPalette.HighlightedText, QColor(115, 115, 115))
|
||||
app.setPalette(lightPalette)
|
||||
_apply_base_theme(app)
|
||||
159
temp_backup/Server_redundant/tempdemo/psmark/myskin_windows.py
Normal file
@@ -0,0 +1,159 @@
|
||||
from os.path import join, dirname, abspath
|
||||
from qtpy.QtCore import Qt, QMetaObject, Signal, Slot, QEvent
|
||||
from qtpy.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QToolButton, QLabel, QSizePolicy
|
||||
from PyQt5.QtGui import QIcon, QPixmap
|
||||
import qtpy, platform
|
||||
|
||||
QT_VERSION = tuple((int(v) for v in qtpy.QT_VERSION.split('.')))
|
||||
PLATFORM = platform.system()
|
||||
_FL_STYLESHEET = 'frameless.qss'
|
||||
|
||||
class WindowDragger(QWidget):
|
||||
'''Window dragger.
|
||||
Args:
|
||||
window (QWidget): Associated window.
|
||||
parent (QWidget, optional): Parent widget.
|
||||
'''
|
||||
doubleClicked = Signal()
|
||||
|
||||
def __init__(self, window, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self._window = window
|
||||
self._mousePressed = False
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
self._mousePressed = True
|
||||
self._mousePos = event.globalPos()
|
||||
self._windowPos = self._window.pos()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if self._mousePressed:
|
||||
self._window.move(self._windowPos + (event.globalPos() - self._mousePos))
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
self._mousePressed = False
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
self.doubleClicked.emit()
|
||||
|
||||
|
||||
class ModernWindow(QWidget):
|
||||
'''
|
||||
Modern window.
|
||||
Args:
|
||||
w (QWidget): Main widget.
|
||||
parent (QWidget, optional): Parent widget.
|
||||
'''
|
||||
|
||||
def __init__(self, w, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self._w = w
|
||||
self.setupUi()
|
||||
contentLayout = QHBoxLayout()
|
||||
contentLayout.setContentsMargins(0, 0, 0, 0)
|
||||
contentLayout.addWidget(w)
|
||||
self.windowContent.setLayout(contentLayout)
|
||||
self.setWindowTitle(w.windowTitle())
|
||||
self.setGeometry(w.geometry())
|
||||
self._w.setAttribute(Qt.WA_DeleteOnClose, True)
|
||||
self._w.destroyed.connect(self._ModernWindow__child_was_closed)
|
||||
|
||||
def setupUi(self):
|
||||
self.vboxWindow = QVBoxLayout(self)
|
||||
self.vboxWindow.setContentsMargins(0, 0, 0, 0)
|
||||
self.windowFrame = QWidget(self)
|
||||
self.windowFrame.setObjectName('windowFrame')
|
||||
self.vboxFrame = QVBoxLayout(self.windowFrame)
|
||||
self.vboxFrame.setContentsMargins(0, 0, 0, 0)
|
||||
self.titleBar = WindowDragger(self, self.windowFrame)
|
||||
self.titleBar.setObjectName('titleBar')
|
||||
self.titleBar.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
|
||||
self.hboxTitle = QHBoxLayout(self.titleBar)
|
||||
self.hboxTitle.setContentsMargins(0, 0, 0, 0)
|
||||
self.hboxTitle.setSpacing(0)
|
||||
titleIcon = QPixmap('./ui/icon.png')
|
||||
Icon = QLabel()
|
||||
Icon.setPixmap(titleIcon.scaled(30, 30))
|
||||
self.icon = Icon
|
||||
self.icon.setAlignment(Qt.AlignLeft)
|
||||
self.lblTitle = QLabel('Title')
|
||||
self.lblTitle.setObjectName('lblTitle')
|
||||
self.lblTitle.setAlignment(Qt.AlignVCenter)
|
||||
spButtons = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
self.btnMinimize = QToolButton(self.titleBar)
|
||||
self.btnMinimize.setObjectName('btnMinimize')
|
||||
self.btnMinimize.setSizePolicy(spButtons)
|
||||
self.btnRestore = QToolButton(self.titleBar)
|
||||
self.btnRestore.setObjectName('btnRestore')
|
||||
self.btnRestore.setSizePolicy(spButtons)
|
||||
self.btnRestore.setVisible(False)
|
||||
self.btnMaximize = QToolButton(self.titleBar)
|
||||
self.btnMaximize.setObjectName('btnMaximize')
|
||||
self.btnMaximize.setSizePolicy(spButtons)
|
||||
self.btnClose = QToolButton(self.titleBar)
|
||||
self.btnClose.setObjectName('btnClose')
|
||||
self.btnClose.setSizePolicy(spButtons)
|
||||
self.vboxFrame.addWidget(self.titleBar)
|
||||
self.windowContent = QWidget(self.windowFrame)
|
||||
self.vboxFrame.addWidget(self.windowContent)
|
||||
self.vboxWindow.addWidget(self.windowFrame)
|
||||
PLATFORM = '1Darwin'
|
||||
if PLATFORM == 'Darwin':
|
||||
self.hboxTitle.addWidget(self.btnClose)
|
||||
self.hboxTitle.addWidget(self.btnMinimize)
|
||||
self.hboxTitle.addWidget(self.btnRestore)
|
||||
self.hboxTitle.addWidget(self.btnMaximize)
|
||||
self.hboxTitle.addWidget(self.lblTitle)
|
||||
else:
|
||||
self.hboxTitle.addWidget(self.icon)
|
||||
self.hboxTitle.addWidget(self.lblTitle)
|
||||
self.hboxTitle.addWidget(self.btnMinimize)
|
||||
self.hboxTitle.addWidget(self.btnRestore)
|
||||
self.hboxTitle.addWidget(self.btnMaximize)
|
||||
self.hboxTitle.addWidget(self.btnClose)
|
||||
self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint)
|
||||
with open(_FL_STYLESHEET) as (stylesheet):
|
||||
self.setStyleSheet(stylesheet.read())
|
||||
QMetaObject.connectSlotsByName(self)
|
||||
|
||||
def __child_was_closed(self):
|
||||
self._w = None
|
||||
self.close()
|
||||
|
||||
def closeEvent(self, event):
|
||||
if not self._w:
|
||||
event.accept()
|
||||
else:
|
||||
self._w.close()
|
||||
event.setAccepted(self._w.isHidden())
|
||||
|
||||
def setWindowTitle(self, title):
|
||||
super(ModernWindow, self).setWindowTitle(title)
|
||||
self.lblTitle.setText(title)
|
||||
|
||||
@Slot()
|
||||
def on_btnMinimize_clicked(self):
|
||||
self.setWindowState(Qt.WindowMinimized)
|
||||
|
||||
@Slot()
|
||||
def on_btnRestore_clicked(self):
|
||||
self.btnRestore.setVisible(False)
|
||||
self.btnMaximize.setVisible(True)
|
||||
self.setWindowState(Qt.WindowNoState)
|
||||
|
||||
@Slot()
|
||||
def on_btnMaximize_clicked(self):
|
||||
self.btnRestore.setVisible(True)
|
||||
self.btnMaximize.setVisible(False)
|
||||
self.setWindowState(Qt.WindowMaximized)
|
||||
|
||||
@Slot()
|
||||
def on_btnClose_clicked(self):
|
||||
self.close()
|
||||
|
||||
@Slot()
|
||||
def on_titleBar_doubleClicked(self):
|
||||
if self.btnMaximize.isVisible():
|
||||
self.on_btnMaximize_clicked()
|
||||
else:
|
||||
self.on_btnRestore_clicked()
|
||||
351
temp_backup/Server_redundant/tempdemo/psmark/newMark.py
Normal file
@@ -0,0 +1,351 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QTabWidget, QPushButton, QLabel, QVBoxLayout, QWidget, QHBoxLayout, QFrame, QMessageBox
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtCore import Qt
|
||||
from Tab1 import ImportPDFDialog
|
||||
from Tab2 import YourMainWindow
|
||||
from Tab3 import ImportPDFDialog2
|
||||
from Tab6 import ImportPDFDialog6
|
||||
from Tab4 import ImportPDFDialog4
|
||||
from test5 import YourMainWindow9
|
||||
import qdarktheme
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
import hashlib
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QGroupBox, \
|
||||
QSpacerItem, QSizePolicy, QMessageBox
|
||||
import pymysql
|
||||
import piece_decorative
|
||||
import configparser
|
||||
|
||||
def exception_hook(exctype, value, traceback):
|
||||
# Handle the uncaught exception
|
||||
# 处理未捕获的异常
|
||||
QMessageBox.warning(None, "错误", f"发生了未知的异常:{value}")
|
||||
|
||||
# class LoginDialog(QWidget):
|
||||
# def __init__(self):
|
||||
# super().__init__()
|
||||
# self.setWindowTitle("PSMARK登录界面")
|
||||
# self.setWindowIcon(QIcon("icons/newapp.ico")) # 设置窗口小图标,替换为您的图标文件路径
|
||||
#
|
||||
# self.resize(300, 200)
|
||||
#
|
||||
# 主布局 = QVBoxLayout()
|
||||
#
|
||||
# group1 = QGroupBox("登录验证")
|
||||
# group1_layout = QVBoxLayout()
|
||||
#
|
||||
# group2 = QHBoxLayout()
|
||||
# label1 = QLabel("用户名")
|
||||
# self.edit1 = QLineEdit()
|
||||
# self.edit1.setFixedWidth(200)
|
||||
# spacer1 = QSpacerItem(40, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
# group2.addWidget(label1)
|
||||
# group2.addItem(spacer1)
|
||||
# group2.addWidget(self.edit1)
|
||||
#
|
||||
# group3 = QHBoxLayout()
|
||||
# label2 = QLabel("密码")
|
||||
# self.edit2 = QLineEdit()
|
||||
# self.edit2.setFixedWidth(200)
|
||||
# self.edit2.setEchoMode(QLineEdit.Password) # 设置密码输入框为密文
|
||||
# spacer2 = QSpacerItem(40, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
# group3.addWidget(label2)
|
||||
# group3.addItem(spacer2)
|
||||
# group3.addWidget(self.edit2)
|
||||
#
|
||||
#
|
||||
#
|
||||
# group4 = QHBoxLayout()
|
||||
# button1 = QPushButton("登录")
|
||||
# button2 = QPushButton("注册")
|
||||
# group4.addWidget(button1)
|
||||
# group4.addWidget(button2)
|
||||
#
|
||||
# group1_layout.addLayout(group2)
|
||||
# group1_layout.addLayout(group3)
|
||||
# group1_layout.addLayout(group4)
|
||||
# group1.setLayout(group1_layout)
|
||||
#
|
||||
# 主布局.addWidget(group1)
|
||||
#
|
||||
# group5 = QHBoxLayout()
|
||||
# label3 = QLabel("机器码")
|
||||
# self.edit3 = QLineEdit()
|
||||
# self.edit3.setFixedWidth(200)
|
||||
#
|
||||
# # 获取主板序列号并提取数字部分
|
||||
# try:
|
||||
# result = subprocess.run(['wmic', 'baseboard', 'get', 'serialnumber'], stdout=subprocess.PIPE,
|
||||
# stderr=subprocess.PIPE, text=True)
|
||||
# motherboard_serial = result.stdout.strip()
|
||||
#
|
||||
# # 使用正则表达式提取数字
|
||||
# motherboard_serial = re.sub(r'\D', '', motherboard_serial)
|
||||
#
|
||||
# # 使用SHA-256加密特征码
|
||||
# feature_code = hashlib.sha256(motherboard_serial.encode()).hexdigest()
|
||||
#
|
||||
# # 去掉特征码中的英文字符
|
||||
# feature_code = re.sub(r'[a-zA-Z]', '', feature_code)
|
||||
# except Exception as e:
|
||||
# feature_code = "Error: " + str(e)
|
||||
#
|
||||
# self.edit3.setText(feature_code) # 将加密后的特征码设置为 "特征码" 输入框的文本
|
||||
# self.rem_user()
|
||||
#
|
||||
# spacer3 = QSpacerItem(10, 10, QSizePolicy.Fixed, QSizePolicy.Minimum)
|
||||
# group5.addWidget(label3)
|
||||
# group5.addItem(spacer3)
|
||||
# group5.addWidget(self.edit3)
|
||||
#
|
||||
# 主布局.addLayout(group5)
|
||||
#
|
||||
# self.setLayout(主布局)
|
||||
#
|
||||
# # 链接登录的点击事件
|
||||
# button1.clicked.connect(self.slot_login)
|
||||
# # 连接注册按钮的点击事件
|
||||
# button2.clicked.connect(self.show_warning_message)
|
||||
#
|
||||
# #记住密码
|
||||
# def rem_user(self):
|
||||
#
|
||||
# piece_decorative.config = configparser.ConfigParser()
|
||||
# piece_decorative.config.read('config.ini', encoding='utf-8')
|
||||
# piece_decorative.PSname = piece_decorative.config.get('程序配置', 'PSname')
|
||||
# self.window = MainWindow()
|
||||
# self.window.show()
|
||||
# self.close()
|
||||
# return
|
||||
#
|
||||
# code = self.edit3.text()
|
||||
# # 执行SQL语句,从user数据表中查询字段值
|
||||
# cur.execute(f"SELECT username,password,code FROM {User}")
|
||||
# # 将数据库查询的结果保存在result中
|
||||
# result = cur.fetchall()
|
||||
# code_list = [it[2] for it in result] # 从数据库查询的result中遍历查询元组中第3个元素code
|
||||
# if code in code_list:
|
||||
# user_name = result[code_list.index(code)][0]
|
||||
# user_password = result[code_list.index(code)][1]
|
||||
# self.edit1.setText(user_name)
|
||||
# self.edit2.setText(user_password)
|
||||
# else:
|
||||
# pass
|
||||
#
|
||||
# def slot_login(self):
|
||||
# user_name = self.edit1.text()
|
||||
# user_password = self.edit2.text()
|
||||
# code = self.edit3.text()
|
||||
# # print(user_name,user_password)
|
||||
# # 执行SQL语句,从user数据表中查询code和time字段值
|
||||
# cur.execute(f"SELECT username,password,code FROM {User}")
|
||||
# # 将数据库查询的结果保存在result中
|
||||
# result = cur.fetchall()
|
||||
# name_list = [it[0] for it in result] # 从数据库查询的result中遍历查询元组中第一个元素name
|
||||
# # 判断用户名或密码不能为空
|
||||
# if not (user_name and user_password):
|
||||
# QMessageBox.critical(self, "错误", "用户名或密码不能为空!")
|
||||
# # 判断用户名和密码是否匹配
|
||||
# elif user_name in name_list:
|
||||
# if user_password == result[name_list.index(user_name)][1]:
|
||||
# if code == result[name_list.index(user_name)][2]:
|
||||
# piece_decorative.config = configparser.ConfigParser()
|
||||
# piece_decorative.config.read('config.ini', encoding='utf-8')
|
||||
# piece_decorative.PSname = piece_decorative.config.get('程序配置', 'PSname')
|
||||
# self.window = MainWindow()
|
||||
# self.window.show()
|
||||
# self.close()
|
||||
# else:
|
||||
# QMessageBox.critical(self, "错误", "机器码不匹配!")
|
||||
# # QMessageBox.information(self, "欢迎您", "登录成功!\n在此添加新界面!")
|
||||
# else:
|
||||
# QMessageBox.critical(self, "错误", "密码输入错误!")
|
||||
# # 账号不在数据库中,则弹出是否注册的框
|
||||
# else:
|
||||
# QMessageBox.critical(self, "错误", "该账号不存在,请注册!")
|
||||
#
|
||||
# def show_warning_message(self):
|
||||
# # 弹出警告消息框
|
||||
# QMessageBox.critical(self, "错误", "请联系管理员 17520145271!")
|
||||
# # warning_message = QMessageBox()
|
||||
# # warning_message.setIcon(QMessageBox.Warning)
|
||||
# # warning_message.setWindowTitle("警告")
|
||||
# # warning_message.setText("请联系管理员 17520145271")
|
||||
# # warning_message.exec_()
|
||||
|
||||
#################################自定义tab标签#################################
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||
|
||||
class MyTabBar(QtWidgets.QTabBar):
|
||||
def paintEvent(self, event):
|
||||
painter = QtWidgets.QStylePainter(self)
|
||||
option = QtWidgets.QStyleOptionTab()
|
||||
for index in range(self.count()):
|
||||
self.initStyleOption(option, index)
|
||||
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, option)
|
||||
painter.drawText(
|
||||
self.tabRect(index),
|
||||
QtCore.Qt.AlignCenter | QtCore.Qt.TextDontClip,
|
||||
"\n".join(self.tabText(index)))
|
||||
|
||||
def tabSizeHint(self, index):
|
||||
#size = QtWidgets.QTabBar.tabSizeHint(self, index)
|
||||
width = max([QtWidgets.QTabBar.fontMetrics(self).width(text) for text in self.tabText(index)])
|
||||
height = len(self.tabText(index)) * QtWidgets.QTabBar.fontMetrics(self).lineSpacing()
|
||||
return QtCore.QSize(width + 12, height + 12)
|
||||
|
||||
class TabWidget(QtWidgets.QTabWidget):
|
||||
def __init__(self, parent=None):
|
||||
QtWidgets.QTabWidget.__init__(self, parent)
|
||||
self.setTabBar(MyTabBar())
|
||||
#################################自定义tab标签#################################
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.setWindowTitle("PS Mark")
|
||||
self.setWindowIcon(QIcon("icons/newapp.ico")) # 设置窗口小图标,替换为您的图标文件路径
|
||||
|
||||
main_widget = QWidget()
|
||||
self.setCentralWidget(main_widget)
|
||||
|
||||
main_layout = QVBoxLayout()
|
||||
main_widget.setLayout(main_layout)
|
||||
|
||||
tab_widget = TabWidget()
|
||||
tab_widget.setTabPosition(TabWidget.West)
|
||||
#tab_widget.tabBar().setStyle(TabBarStyle(Qt.Vertical))
|
||||
main_layout.addWidget(tab_widget)
|
||||
|
||||
tab2 = YourMainWindow()
|
||||
tab_widget.addTab(tab2, "纸样分割")
|
||||
|
||||
tab1 = ImportPDFDialog()
|
||||
tab_widget.addTab(tab1, "裁片套版")
|
||||
#
|
||||
#
|
||||
tab3 = ImportPDFDialog2()
|
||||
tab_widget.addTab(tab3, "快速换图")
|
||||
|
||||
# tab6 = ImportPDFDialog6()
|
||||
# tab_widget.addTab(tab6, "ai重绘")
|
||||
#
|
||||
# tab3 = YourMainWindow9()
|
||||
# tab_widget.addTab(tab3, "快速换图")
|
||||
|
||||
# tab4 = ImportPDFDialog4()
|
||||
# tab_widget.addTab(tab4, "版本介绍")
|
||||
#
|
||||
# separator = QFrame()
|
||||
# separator.setFrameShape(QFrame.HLine)
|
||||
# separator.setFrameShadow(QFrame.Sunken)
|
||||
# main_layout.addWidget(separator)
|
||||
|
||||
#custom_label = QLabel("尊敬的公司内部体验版用户(1.8.5),欢迎使用!!!")
|
||||
#############8-12号修复了宽高缩放跟比例缩放超过10个裁片就会定位不准的bug
|
||||
#############8-12号将缩水值跟前缀添加改为了前置条件
|
||||
#############8-24号新增Tab2界面 将DXF解析与CDR结合在一起 可以实现分段排版
|
||||
|
||||
|
||||
# main_layout.addWidget(custom_label)
|
||||
|
||||
self.setLayout(main_layout)
|
||||
# # self.setWindowTitle("分割线和自定义文字示例")
|
||||
# settings_button = QPushButton(QIcon("icons/转换.png"), "")
|
||||
|
||||
# settings_button.setObjectName("settingsButton") # 设置对象名称以供样式表选择
|
||||
# settings_button.setStyleSheet("border: none;") # 设置按钮无边框
|
||||
# main_layout.addWidget(settings_button, alignment=Qt.AlignLeft)
|
||||
|
||||
# 设置窗口始终置顶
|
||||
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
||||
|
||||
self.width_ratio = 0.3
|
||||
self.height_ratio = 1
|
||||
|
||||
self.initial_width = self.width()
|
||||
self.initial_height = self.height()
|
||||
|
||||
# def resizeEvent(self, event):
|
||||
# super().resizeEvent(event)
|
||||
#
|
||||
# # 获取当前窗口的宽度和高度
|
||||
# current_width = self.width()
|
||||
# current_height = self.height()
|
||||
#
|
||||
# # 判断当前窗口是否超过了初始大小
|
||||
# if current_width > self.initial_width or current_height > self.initial_height:
|
||||
# # 允许窗口继续拉伸,不设置最小宽度和最小高度
|
||||
# pass
|
||||
# else:
|
||||
# # 重新设置最小宽度和最小高度为初始大小
|
||||
# self.setMinimumWidth(self.initial_width)
|
||||
# self.setMinimumHeight(self.initial_height)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
|
||||
# 获取当前窗口的宽度和高度
|
||||
current_width = self.width()
|
||||
current_height = self.height()
|
||||
|
||||
# 如果是第一次调整大小,更新初始大小
|
||||
if not hasattr(self, 'initial_size_set') or not self.initial_size_set:
|
||||
self.initial_width = current_width
|
||||
self.initial_height = current_height
|
||||
self.initial_size_set = True # 标记初始大小已被设置
|
||||
|
||||
# 判断当前窗口是否超过了初始大小
|
||||
if current_width > self.initial_width or current_height > self.initial_height:
|
||||
# 允许窗口继续拉伸,不设置最小宽度和最小高度
|
||||
pass
|
||||
else:
|
||||
# 重新设置最小宽度和最小高度为初始大小
|
||||
self.setMinimumWidth(self.initial_width)
|
||||
self.setMinimumHeight(self.initial_height)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
host = "rm-bp1s36ps814qp23b7uo.mysql.rds.aliyuncs.com"
|
||||
user = "zw1847930177"
|
||||
password = "Zuowei1216"
|
||||
database = "program"
|
||||
charset = "utf8"
|
||||
port = 3306
|
||||
db = pymysql.connect(host=host, user=user, password=password, database=database, charset=charset, port=port)
|
||||
cur = db.cursor()
|
||||
user_creat ="""
|
||||
CREATE TABLE IF NOT EXISTS User(
|
||||
`id` INT auto_increment PRIMARY KEY,
|
||||
`username` varchar(255) DEFAULT '',
|
||||
`password` varchar(255) DEFAULT '',
|
||||
`code` varchar(255) DEFAULT ''
|
||||
) ENGINE=innodb DEFAULT CHARSET=utf8;
|
||||
"""
|
||||
cur.execute(user_creat)
|
||||
cur = db.cursor()
|
||||
User = 'User'
|
||||
|
||||
app3 = QApplication(sys.argv)
|
||||
sys.excepthook = exception_hook # 设置全局异常处理
|
||||
|
||||
qdarktheme.setup_theme(
|
||||
custom_colors={
|
||||
"[dark]": {
|
||||
"background": "#4d4d4d",
|
||||
"foreground": "#ffffff",
|
||||
"primary": "#ffffff",
|
||||
"border": "#717070",
|
||||
}
|
||||
}
|
||||
)
|
||||
# login_dialog = LoginDialog()
|
||||
# login_dialog.show()
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
sys.exit(app3.exec_())
|
||||
51
temp_backup/Server_redundant/tempdemo/psmark/newMark.spec
Normal file
@@ -0,0 +1,51 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['newMark.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='newMark',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['newapp.ico'],
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='newMark',
|
||||
)
|
||||
BIN
temp_backup/Server_redundant/tempdemo/psmark/newapp.ico
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
142
temp_backup/Server_redundant/tempdemo/psmark/newmarkLogin.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
import hashlib
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QGroupBox, \
|
||||
QSpacerItem, QSizePolicy, QMessageBox
|
||||
import pymysql
|
||||
host = "rm-bp1s36ps814qp23b7uo.mysql.rds.aliyuncs.com"
|
||||
user = "zw1847930177"
|
||||
password = "Zuowei1216"
|
||||
database = "program"
|
||||
charset = "utf8"
|
||||
port = 3306
|
||||
db = pymysql.connect(host=host, user=user, password=password, database=database, charset=charset, port=port)
|
||||
cur = db.cursor()
|
||||
user_creat ="""
|
||||
CREATE TABLE IF NOT EXISTS User(
|
||||
`id` INT auto_increment PRIMARY KEY,
|
||||
`username` varchar(255) DEFAULT '',
|
||||
`password` varchar(255) DEFAULT ''
|
||||
) ENGINE=innodb DEFAULT CHARSET=utf8;
|
||||
"""
|
||||
cur.execute(user_creat)
|
||||
cur = db.cursor()
|
||||
User = 'User'
|
||||
|
||||
class LoginDialog(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("PSMARK登录界面")
|
||||
self.resize(300, 200)
|
||||
|
||||
主布局 = QVBoxLayout()
|
||||
|
||||
group1 = QGroupBox("登录验证")
|
||||
group1_layout = QVBoxLayout()
|
||||
|
||||
group2 = QHBoxLayout()
|
||||
label1 = QLabel("用户名")
|
||||
self.edit1 = QLineEdit()
|
||||
self.edit1.setFixedWidth(120)
|
||||
spacer1 = QSpacerItem(40, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
group2.addWidget(label1)
|
||||
group2.addItem(spacer1)
|
||||
group2.addWidget(self.edit1)
|
||||
|
||||
group3 = QHBoxLayout()
|
||||
label2 = QLabel("密码")
|
||||
self.edit2 = QLineEdit()
|
||||
self.edit2.setFixedWidth(120)
|
||||
spacer2 = QSpacerItem(40, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
group3.addWidget(label2)
|
||||
group3.addItem(spacer2)
|
||||
group3.addWidget(self.edit2)
|
||||
|
||||
group4 = QHBoxLayout()
|
||||
button1 = QPushButton("登录")
|
||||
button2 = QPushButton("注册")
|
||||
group4.addWidget(button1)
|
||||
group4.addWidget(button2)
|
||||
|
||||
group1_layout.addLayout(group2)
|
||||
group1_layout.addLayout(group3)
|
||||
group1_layout.addLayout(group4)
|
||||
group1.setLayout(group1_layout)
|
||||
|
||||
主布局.addWidget(group1)
|
||||
|
||||
group5 = QHBoxLayout()
|
||||
label3 = QLabel("机器码")
|
||||
edit3 = QLineEdit()
|
||||
edit3.setFixedWidth(200)
|
||||
|
||||
# 获取主板序列号并提取数字部分
|
||||
try:
|
||||
result = subprocess.run(['wmic', 'baseboard', 'get', 'serialnumber'], stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, text=True)
|
||||
motherboard_serial = result.stdout.strip()
|
||||
|
||||
# 使用正则表达式提取数字
|
||||
motherboard_serial = re.sub(r'\D', '', motherboard_serial)
|
||||
|
||||
# 使用SHA-256加密特征码
|
||||
feature_code = hashlib.sha256(motherboard_serial.encode()).hexdigest()
|
||||
|
||||
# 去掉特征码中的英文字符
|
||||
feature_code = re.sub(r'[a-zA-Z]', '', feature_code)
|
||||
except Exception as e:
|
||||
feature_code = "Error: " + str(e)
|
||||
|
||||
edit3.setText(feature_code) # 将加密后的特征码设置为 "特征码" 输入框的文本
|
||||
|
||||
spacer3 = QSpacerItem(10, 10, QSizePolicy.Fixed, QSizePolicy.Minimum)
|
||||
group5.addWidget(label3)
|
||||
group5.addItem(spacer3)
|
||||
group5.addWidget(edit3)
|
||||
|
||||
主布局.addLayout(group5)
|
||||
|
||||
self.setLayout(主布局)
|
||||
|
||||
# 链接登录的点击事件
|
||||
button1.clicked.connect(self.slot_login)
|
||||
# 连接注册按钮的点击事件
|
||||
button2.clicked.connect(self.show_warning_message)
|
||||
|
||||
def slot_login(self):
|
||||
user_name = self.edit1.text()
|
||||
user_password = self.edit2.text()
|
||||
# print(user_name,user_password)
|
||||
# 执行SQL语句,从user数据表中查询code和time字段值
|
||||
cur.execute(f"SELECT username,password FROM {User}")
|
||||
# 将数据库查询的结果保存在result中
|
||||
result = cur.fetchall()
|
||||
name_list = [it[0] for it in result] # 从数据库查询的result中遍历查询元组中第一个元素name
|
||||
# 判断用户名或密码不能为空
|
||||
if not (user_name and user_password):
|
||||
QMessageBox.critical(self, "错误", "用户名或密码不能为空!")
|
||||
# 判断用户名和密码是否匹配
|
||||
elif user_name in name_list:
|
||||
if user_password == result[name_list.index(user_name)][1]:
|
||||
QMessageBox.information(self, "欢迎您", "登录成功!\n在此添加新界面!")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "密码输入错误!")
|
||||
# 账号不在数据库中,则弹出是否注册的框
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "该账号不存在,请注册!")
|
||||
|
||||
def show_warning_message(self):
|
||||
# 弹出警告消息框
|
||||
warning_message = QMessageBox()
|
||||
warning_message.setIcon(QMessageBox.Warning)
|
||||
warning_message.setWindowTitle("警告")
|
||||
warning_message.setText("请联系管理员 17520145271")
|
||||
warning_message.exec_()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
login_dialog = LoginDialog()
|
||||
login_dialog.show()
|
||||
sys.exit(app.exec_())
|
||||
299
temp_backup/Server_redundant/tempdemo/psmark/piece_decorative.py
Normal file
@@ -0,0 +1,299 @@
|
||||
import configparser
|
||||
from win32com.client import Dispatch
|
||||
from JSX2 import dxf3_jscode
|
||||
from JSX3 import dxf2_jscode
|
||||
from JSX1 import dxf_jscode
|
||||
from JSX4 import dxf4_jscode
|
||||
from JSX5 import dxf5_jscode
|
||||
from JSX6 import dxf6_jscode
|
||||
from JSX7 import dxf7_jscode
|
||||
from JSX8 import dxf8_jscode
|
||||
from JSX9 import dxf9_jscode
|
||||
from JSX10 import dxf10_jscode
|
||||
from JSX11 import dxf11_jscode
|
||||
from JSX12 import dxf12_jscode
|
||||
from JSX13 import dxf13_jscode
|
||||
from JSX14 import dxf14_jscode
|
||||
from JSX15 import dxf15_jscode
|
||||
from JSX16 import dxf16_jscode
|
||||
from JSX17 import dxf17_jscode
|
||||
from JSX18 import dxf18_jscode
|
||||
from JSX19 import dxf19_jscode
|
||||
from JSX20 import dxf20_jscode
|
||||
from JSX21 import dxf21_jscode
|
||||
from JSX22 import dxf22_jscode
|
||||
from JSX23 import dxf23_jscode
|
||||
from JSX24 import dxf24_jscode
|
||||
from JSX25 import dxf25_jscode
|
||||
from JSX26 import dxf26_jscode
|
||||
from JSX27 import dxf27_jscode
|
||||
|
||||
|
||||
psapp = None
|
||||
aiapp = None
|
||||
config = configparser.ConfigParser()
|
||||
config.read('程序配置.ini', encoding='utf-8')
|
||||
PSname = config.get('程序配置', 'ps应用名')
|
||||
|
||||
# from datetime import datetime # 引入datetime,获取当前日期
|
||||
# import sys # 引用退出程序方法
|
||||
#
|
||||
# ## 逻辑实现
|
||||
# d1 = datetime.now().date()
|
||||
# d2 = pd.to_datetime('2023-9-15').date()
|
||||
#
|
||||
# print("当前日期:", d1)
|
||||
# print("限制日期:", d2)
|
||||
#
|
||||
# if d1 > d2:
|
||||
# print('软件已过期,请联系作者!')
|
||||
# sys.exit()
|
||||
def PS_DXF_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
|
||||
def PS_DXF2_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf2_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF3_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf3_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF4_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf4_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF5_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf5_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF6_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf6_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF7_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf7_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF8_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf8_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF9_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf9_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def PS_DXF10_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf10_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF11_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf11_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF12_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf12_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF13_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf13_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF14_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf14_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def PS_DXF15_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf15_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF16_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf16_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF17_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf17_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def PS_DXF18_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf18_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF19_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf19_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF20_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf20_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def PS_DXF21_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf21_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF22_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf22_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def PS_DXF23_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf23_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF24_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf24_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF25_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf25_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
def PS_DXF26_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf26_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
def PS_DXF27_jscode_fun(funcode):
|
||||
print(funcode)
|
||||
global psapp
|
||||
if psapp is None:
|
||||
psapp = Dispatch(PSname)
|
||||
res = psapp.DoJavaScript(dxf27_jscode + '\n' + funcode)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
|
||||
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
55
temp_backup/Server_redundant/tempdemo/psmark/test.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout
|
||||
import piece_decorative
|
||||
class SimpleApp(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 初始化界面
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
# 创建一个按钮
|
||||
btn = QPushButton('接口测试', self)
|
||||
|
||||
# 连接按钮的点击事件到槽函数
|
||||
btn.clicked.connect(self.button_click)
|
||||
|
||||
# 创建一个垂直布局
|
||||
layout = QVBoxLayout()
|
||||
|
||||
# 在布局中添加按钮
|
||||
layout.addWidget(btn)
|
||||
|
||||
# 设置主窗口的布局
|
||||
self.setLayout(layout)
|
||||
|
||||
# 设置主窗口的标题和大小
|
||||
self.setWindowTitle('测试')
|
||||
self.setGeometry(300, 300, 300, 200)
|
||||
|
||||
# 显示界面
|
||||
self.show()
|
||||
|
||||
def button_click(self):
|
||||
# 按钮点击时调用的槽函数
|
||||
|
||||
# piece_decorative.PS_DXF2_jscode_fun('设置花样组删除图层设置名称();')
|
||||
# piece_decorative.PS_DXF12_jscode_fun('批量套数写入();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('设置花样组2();')
|
||||
piece_decorative.PS_DXF19_jscode_fun('裁片抓取新的();')
|
||||
piece_decorative.PS_DXF2_jscode_fun('设置花样组顺序居中();')
|
||||
piece_decorative.PS_DXF_jscode_fun('信息写入();')
|
||||
piece_decorative.PS_DXF3_jscode_fun('裁片视图检查2();')
|
||||
print('按钮被点击了!')
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 创建应用程序对象
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
# 创建主窗口
|
||||
window = SimpleApp()
|
||||
|
||||
# 运行应用程序的主循环
|
||||
sys.exit(app.exec_())
|
||||
|
||||
324
temp_backup/Server_redundant/tempdemo/psmark/test2.py
Normal file
@@ -0,0 +1,324 @@
|
||||
import myskin_styles, myskin_windows
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5.QtGui import *
|
||||
import sys, os
|
||||
import piece_decorative
|
||||
import ezdxf
|
||||
import ezdxf.tools
|
||||
import ezdxf.bbox
|
||||
import ezdxf.units
|
||||
import ezdxf.math
|
||||
import qdarktheme
|
||||
|
||||
import re
|
||||
def extract_number_from_block_name(block_name):
|
||||
# 使用正则表达式提取块名称中的数字部分
|
||||
match = re.search(r'P(\d+)', block_name)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
return 0 # 如果没有找到数字,默认返回0
|
||||
|
||||
|
||||
|
||||
|
||||
def 获取dxf中心坐标和角度列表(doc):
|
||||
msp = doc.modelspace()
|
||||
mspBox = ezdxf.bbox.extents(msp)
|
||||
result = []
|
||||
for entity in msp.query():
|
||||
if entity.dxftype() == "INSERT":
|
||||
result.append({"块名称": entity.dxf.name, "center": None, "rotation": None})
|
||||
|
||||
temp = []
|
||||
rotation = None
|
||||
block = doc.blocks[entity.dxf.name]
|
||||
for e in block:
|
||||
if e.dxftype() != "TEXT":
|
||||
temp.append(e)
|
||||
else:
|
||||
rotation = e.dxf.all_existing_dxf_attribs()["rotation"]
|
||||
# 只有 0 90 -90 -180 出现其他的就以近似值代替
|
||||
if rotation < 0:
|
||||
rotation += 360
|
||||
if rotation < 45 or rotation > 315:
|
||||
rotation = 0
|
||||
elif rotation < 135:
|
||||
rotation = 90
|
||||
elif rotation < 225:
|
||||
rotation = -180
|
||||
else:
|
||||
rotation = -90
|
||||
|
||||
result[-1]["rotation"] = rotation
|
||||
|
||||
center = ezdxf.bbox.extents(temp).center
|
||||
# center y 改成从上往下计算
|
||||
center = (center.x, mspBox.size[1] + mspBox.extmin[1] - center.y)
|
||||
# 旋转
|
||||
center = (mspBox.size[1] - center[1], center[0])
|
||||
|
||||
result[-1]["center"] = center
|
||||
|
||||
# 打印处理前的数据
|
||||
print("处理前的数据:", result)
|
||||
|
||||
# 按块名称进行排序
|
||||
result.sort(key=lambda x: extract_number_from_block_name(x["块名称"]))
|
||||
|
||||
# 打印处理后的数据
|
||||
print("处理后的数据:", result)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
class DXFWinUi(object):
|
||||
|
||||
def setupUi(self, MainWindow):
|
||||
# MainWindow.resize(800, 600)
|
||||
|
||||
self.centralwidget = QWidget(MainWindow)
|
||||
|
||||
mainLayout = QHBoxLayout(self.centralwidget)
|
||||
|
||||
groupBox = QGroupBox('多码混排')
|
||||
|
||||
leftLayout = QVBoxLayout(groupBox)
|
||||
|
||||
label1 = QLabel('大货裁片路径')
|
||||
# self.label2 = QLabel('暂未选择文件夹。')
|
||||
self.pushButton1 = QPushButton("选择文件夹")
|
||||
self.pushButton1.setFixedSize(100,30)
|
||||
leftLayout.addWidget(label1)
|
||||
# leftLayout.addWidget(self.label2)
|
||||
leftLayout.addWidget(self.pushButton1)
|
||||
leftLayout.addStretch()
|
||||
|
||||
label6 = QLabel('DXF文件路径')
|
||||
# self.label7 = QLabel('暂未选择文件。')
|
||||
self.pushButton2 = QPushButton("选择文件")
|
||||
self.pushButton2.setFixedSize(100, 30)
|
||||
leftLayout.addWidget(label6)
|
||||
# leftLayout.addWidget(self.label7)
|
||||
leftLayout.addWidget(self.pushButton2)
|
||||
leftLayout.addStretch()
|
||||
|
||||
|
||||
label3 = QLabel('分辨率大小')
|
||||
self.lineEdit1 = QLineEdit()
|
||||
self.lineEdit1.setValidator(QIntValidator())
|
||||
self.lineEdit1.setFixedSize(150,30)
|
||||
leftLayout.addWidget(label3)
|
||||
leftLayout.addWidget(self.lineEdit1)
|
||||
leftLayout.addStretch()
|
||||
|
||||
label4 = QLabel('文档名称')
|
||||
self.lineEdit2 = QLineEdit()
|
||||
self.lineEdit2.setFixedSize(150,30)
|
||||
leftLayout.addWidget(label4)
|
||||
leftLayout.addWidget(self.lineEdit2)
|
||||
leftLayout.addStretch()
|
||||
|
||||
label5 = QLabel('单码片数')
|
||||
self.lineEdit3 = QLineEdit()
|
||||
self.lineEdit3.setFixedSize(150,30)
|
||||
self.lineEdit3.setValidator(QIntValidator())
|
||||
leftLayout.addWidget(label5)
|
||||
leftLayout.addWidget(self.lineEdit3)
|
||||
leftLayout.addStretch()
|
||||
|
||||
|
||||
|
||||
# 滚动区域
|
||||
scrollArea = QScrollArea()
|
||||
scrollArea.setFixedSize(200,210)
|
||||
scrollWidget = QWidget(scrollArea)
|
||||
self.scrollWidgetLayout = QVBoxLayout(scrollWidget)
|
||||
self.scrollWidgetLayout.setContentsMargins(5,5,5,5)
|
||||
self.scrollWidgetLayout.setSpacing(5)
|
||||
scrollArea.setWidget(scrollWidget)
|
||||
scrollArea.setWidgetResizable(True)
|
||||
leftLayout.addWidget(scrollArea)
|
||||
leftLayout.addStretch()
|
||||
|
||||
rightWidget = QWidget()
|
||||
|
||||
# rightLayout = QVBoxLayout(rightWidget)
|
||||
# self.okPushButton = QPushButton("OK")
|
||||
# self.cancelPushButton = QPushButton("Cancel")
|
||||
# rightLayout.addWidget(self.okPushButton)
|
||||
# rightLayout.addWidget(self.cancelPushButton)
|
||||
# rightLayout.addStretch()
|
||||
|
||||
# rightWidget.setLayout(rightLayout)
|
||||
self.runButton = QPushButton("运行")
|
||||
leftLayout.addWidget(self.runButton)
|
||||
|
||||
rightWidget = QWidget()
|
||||
|
||||
mainLayout.addWidget(groupBox, 1)
|
||||
# mainLayout.addWidget(rightWidget, 1)
|
||||
|
||||
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
|
||||
|
||||
class DXFWin(QMainWindow, DXFWinUi):
|
||||
|
||||
def __init__(self):
|
||||
QMainWindow.__init__(self)
|
||||
self.ui = DXFWinUi()
|
||||
self.ui.setupUi(self)
|
||||
self.setWindowTitle("水平布局管理例子")
|
||||
|
||||
desktop = QApplication.desktop()
|
||||
self.move((desktop.width() - self.width()) / 2, (desktop.height() - self.height()) /2)
|
||||
|
||||
self.ui.pushButton1.clicked.connect(self.chooseDir)
|
||||
# self.ui.cancelPushButton.clicked.connect(self.close)
|
||||
self.ui.pushButton2.clicked.connect(self.chooseDxf)
|
||||
# self.ui.okPushButton.clicked.connect(self.ok)
|
||||
self.ui.runButton.clicked.connect(self.run)
|
||||
|
||||
self.dir = None
|
||||
self.dxfPath = None
|
||||
self.dxfLineEdits = {}
|
||||
|
||||
def run(self):
|
||||
print("按钮被点击")
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def chooseDir(self):
|
||||
rst = QFileDialog.getExistingDirectory()
|
||||
|
||||
if rst != '':
|
||||
self.ui.label2.setText(rst)
|
||||
self.dir = rst
|
||||
|
||||
def chooseDxf(self):
|
||||
rst = QFileDialog.getOpenFileName(filter='dxf file(*.dxf)')
|
||||
rst = rst[0]
|
||||
|
||||
if rst == '':
|
||||
return
|
||||
|
||||
|
||||
|
||||
# 解析文件路径
|
||||
# self.ui.label7.setText(rst)
|
||||
self.dxfPath = rst
|
||||
|
||||
sizes = []
|
||||
temp = ''
|
||||
for c in os.path.basename(rst).split('.')[0]:
|
||||
import string
|
||||
if c in string.punctuation:
|
||||
if temp != '':
|
||||
sizes.append(temp)
|
||||
temp = ''
|
||||
continue
|
||||
temp += c
|
||||
if temp != '':
|
||||
sizes.append(temp)
|
||||
|
||||
#清空self.ui.scrollWidgetLayout
|
||||
for i in range(self.ui.scrollWidgetLayout.count()):
|
||||
item = self.ui.scrollWidgetLayout.itemAt(0)
|
||||
self.ui.scrollWidgetLayout.removeItem(item)
|
||||
if item.widget():
|
||||
item.widget().deleteLater()
|
||||
self.dxfLineEdits = {}
|
||||
|
||||
# 添加label和button
|
||||
for size in sizes:
|
||||
layout = QHBoxLayout()
|
||||
layout.addWidget(QLabel(size))
|
||||
layout.setContentsMargins(0,0,00,0)
|
||||
lineEdit = QLineEdit()
|
||||
lineEdit.setValidator(QIntValidator())
|
||||
lineEdit.setFixedSize(100, 30)
|
||||
self.dxfLineEdits[size] = lineEdit
|
||||
layout.addWidget(lineEdit)
|
||||
self.ui.scrollWidgetLayout.addLayout(layout)
|
||||
|
||||
def ok(self):
|
||||
# 检查参数
|
||||
if self.dxfPath == None:
|
||||
QMessageBox.critical(self, "错误", "未选择dxf文件!")
|
||||
return
|
||||
if self.dir == None:
|
||||
QMessageBox.critical(self, "错误", "未选择大货裁片文件夹!")
|
||||
return
|
||||
|
||||
# 禁用窗口
|
||||
self.setDisabled(True)
|
||||
|
||||
# 解析dxf数据
|
||||
doc = ezdxf.readfile(self.dxfPath)
|
||||
msp = doc.modelspace()
|
||||
mspBox = ezdxf.bbox.extents(msp)
|
||||
|
||||
画布高 = mspBox.size[0]
|
||||
画布宽 = mspBox.size[1]
|
||||
|
||||
分辨率 = self.ui.lineEdit1.text()
|
||||
文档名称 = self.ui.lineEdit2.text()
|
||||
|
||||
piece_decorative.PS_DXF21_jscode_fun(f'创建裁片排版文档({画布宽},{画布高},{分辨率},"{文档名称}");')
|
||||
单码片数 = int(self.ui.lineEdit3.text())
|
||||
DXFnames = []
|
||||
for size, lineEdit in self.dxfLineEdits.items():
|
||||
for i in range(单码片数):
|
||||
for j in range(int(lineEdit.text())):
|
||||
DXFname = f"P{i + 1}-{size}"
|
||||
DXFnames.append(DXFname)
|
||||
print(DXFnames)
|
||||
|
||||
中心坐标和角度列表 = 获取dxf中心坐标和角度列表(doc)
|
||||
|
||||
|
||||
|
||||
# 保存新的文件
|
||||
# doc.saveas(self.dxfPath + ".out.dxf")
|
||||
|
||||
for i in range(len(DXFnames)):
|
||||
DXFname = DXFnames[i]
|
||||
中心x_mm = 中心坐标和角度列表[i]["center"][0]
|
||||
中心y_mm = 中心坐标和角度列表[i]["center"][1]
|
||||
角度 = 中心坐标和角度列表[i]["rotation"]
|
||||
|
||||
piece_decorative.PS_DXF21_jscode_fun(f'置入链接的智能对象("{self.dir}","{DXFname}");')
|
||||
piece_decorative.PS_DXF21_jscode_fun(f'裁片排版_lay({中心x_mm},{中心y_mm});')
|
||||
piece_decorative.PS_DXF21_jscode_fun(f'裁片角度({角度});')
|
||||
|
||||
|
||||
self.setDisabled(False)
|
||||
|
||||
def main():
|
||||
try:
|
||||
app = QApplication(sys.argv)
|
||||
# myskin_styles.dark(app)
|
||||
win = DXFWin()
|
||||
qdarktheme.setup_theme(
|
||||
custom_colors={
|
||||
"[dark]": {
|
||||
"background": "#4d4d4d",
|
||||
"foreground": "#ffffff",
|
||||
"primary": "#ffffff",
|
||||
"border": "#717070",
|
||||
}
|
||||
}
|
||||
)
|
||||
# mw = myskin_windows.ModernWindow(win)
|
||||
# win.setWindowIcon(QIcon('./ui/app.ico'))
|
||||
win.show()
|
||||
# win.show()
|
||||
sys.exit(app.exec_())
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
11
temp_backup/Server_redundant/tempdemo/psmark/test3.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import json
|
||||
|
||||
# 读取 JSON 文件
|
||||
file_path = 'C:\\Users\\Administrator\\Desktop\\example.json'
|
||||
with open(file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
|
||||
# 处理数据
|
||||
print("name:", data["name"])
|
||||
print("resolution:", data["resolution"])
|
||||
print("matchCount:", data["matchCount"])
|
||||
264
temp_backup/Server_redundant/tempdemo/psmark/test5.py
Normal file
@@ -0,0 +1,264 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QLabel, QPushButton, QFileDialog, \
|
||||
QLineEdit, QScrollArea, QGroupBox, QHBoxLayout, QMessageBox, QProgressDialog
|
||||
from PyQt5.QtGui import QIntValidator
|
||||
from PyQt5.QtCore import QTimer
|
||||
import re
|
||||
import os
|
||||
import ezdxf
|
||||
import ezdxf.tools
|
||||
import ezdxf.bbox
|
||||
import ezdxf.units
|
||||
import ezdxf.math
|
||||
from coreldraw_checker import is_coreldraw_running
|
||||
from functools import partial
|
||||
import win32com.client
|
||||
import os
|
||||
import shutil
|
||||
import threading
|
||||
from clear_folder import another_function
|
||||
|
||||
|
||||
class YourMainWindow9(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.setWindowTitle("RUNDXF")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
self.button2 = QPushButton("裁片文件路径")
|
||||
self.button2.setFixedWidth(200)
|
||||
self.button2.clicked.connect(self.showPltFileDialog)
|
||||
|
||||
self.button1 = QPushButton("DXF文件路径")
|
||||
self.button1.setFixedWidth(200)
|
||||
self.button1.clicked.connect(self.showDxfFileDialog)
|
||||
|
||||
self.initial_button2_text = self.button2.text()
|
||||
|
||||
self.initial_button1_text = self.button1.text()
|
||||
|
||||
panel1 = QGroupBox("文件路径")
|
||||
panel1_layout = QVBoxLayout(panel1)
|
||||
panel1_layout.setSpacing(10)
|
||||
panel1_layout.setContentsMargins(10, 10, 10, 10)
|
||||
panel1_layout.addWidget(self.button2)
|
||||
panel1_layout.addWidget(self.button1)
|
||||
|
||||
layout.addWidget(panel1)
|
||||
|
||||
self.label7 = QLabel('文档名称')
|
||||
self.lineEdit7 = QLineEdit()
|
||||
self.lineEdit7.setValidator(QIntValidator())
|
||||
self.lineEdit7.setFixedSize(120, 30)
|
||||
layout.addWidget(self.label7)
|
||||
layout.addWidget(self.lineEdit7)
|
||||
|
||||
self.label6 = QLabel('分辨率大小')
|
||||
self.lineEdit6 = QLineEdit()
|
||||
self.lineEdit6.setValidator(QIntValidator())
|
||||
self.lineEdit6.setFixedSize(120, 30)
|
||||
layout.addWidget(self.label6)
|
||||
layout.addWidget(self.lineEdit6)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
self.label5 = QLabel('单码片数')
|
||||
self.lineEdit3 = QLineEdit()
|
||||
self.lineEdit3.setValidator(QIntValidator())
|
||||
self.lineEdit3.setFixedSize(120, 30)
|
||||
layout.addWidget(self.label5)
|
||||
layout.addWidget(self.lineEdit3)
|
||||
|
||||
|
||||
self.scrollWidget = QWidget()
|
||||
self.scrollWidgetLayout = QVBoxLayout(self.scrollWidget)
|
||||
self.scrollArea = QScrollArea()
|
||||
self.scrollArea.setWidget(self.scrollWidget)
|
||||
self.scrollArea.setWidgetResizable(True)
|
||||
layout.addWidget(self.scrollArea)
|
||||
#
|
||||
# self.clearButton = QPushButton("清空信息")
|
||||
# self.clearButton.clicked.connect(self.clearScrollArea)
|
||||
# layout.addWidget(self.clearButton)
|
||||
|
||||
# self.bigconfirm_button = QPushButton("混码分割")
|
||||
# self.bigconfirm_button.clicked.connect(self.bigupdateScrollArea)
|
||||
# layout.addWidget(self.bigconfirm_button)
|
||||
|
||||
confirm_button = QPushButton("运行")
|
||||
confirm_button.clicked.connect(self.updateScrollArea)
|
||||
layout.addWidget(confirm_button)
|
||||
|
||||
|
||||
central_widget = QWidget()
|
||||
central_widget.setLayout(layout)
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
self.dxfLineEdits = {}
|
||||
|
||||
self.allowButtonActions = True # 标志变量,控制是否允许按钮行为
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def showPltFileDialog(self):
|
||||
options = QFileDialog.Options()
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, "选择PLT文件", "", "PLT Files (*.plt);;All Files (*)",
|
||||
options=options)
|
||||
if file_path:
|
||||
print("Selected PLT file:", file_path)
|
||||
extracted_content = self.extract_content_from_plt_path(file_path)
|
||||
print(extracted_content)
|
||||
self.sizes = self.fill_sizes_from_extracted_content(extracted_content)
|
||||
|
||||
# 更新尺寸字典后,清空并填充滚动区域
|
||||
self.clearScrollArea()
|
||||
self.populateScrollArea()
|
||||
|
||||
self.initial_plt_path = file_path # 更新初始路径而不更新按钮文本
|
||||
|
||||
else:
|
||||
print("No PLT file selected")
|
||||
QMessageBox.warning(self, "警告", "没有选择文件夹。请重新选择文件夹。", QMessageBox.Ok)
|
||||
|
||||
pass
|
||||
|
||||
def showDxfFileDialog(self):
|
||||
options = QFileDialog.Options()
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, "选择DXF文件", "", "DXF Files (*.dxf);;All Files (*)",
|
||||
options=options)
|
||||
if file_path:
|
||||
print("Selected DXF file:", file_path)
|
||||
extracted_content = self.extract_content_from_plt_path(file_path) # 更新初始路径而不更新按钮文本
|
||||
print(extracted_content)
|
||||
self.sizes = self.fill_sizes_from_extracted_content(extracted_content)
|
||||
|
||||
# 更新尺寸字典后,清空并填充滚动区域
|
||||
self.clearScrollArea()
|
||||
self.populateScrollArea()
|
||||
|
||||
self.initial_plt_path = file_path # 更新初始路径而不更新按钮文本
|
||||
else:
|
||||
print("No DXF file selected")
|
||||
QMessageBox.warning(self, "警告", "没有选择文件夹。请重新选择文件夹。", QMessageBox.Ok)
|
||||
# 在此处添加提醒逻辑,例如使用 QMessageBox 提示用户没有选择文件
|
||||
|
||||
def extract_content_from_plt_path(self, plt_path):
|
||||
match = re.search(r'\((.*?)\)', plt_path)
|
||||
if match:
|
||||
extracted_content = match.group(1)
|
||||
return extracted_content
|
||||
else:
|
||||
return "No content in parentheses found"
|
||||
|
||||
def fill_sizes_from_extracted_content(self, extracted_content):
|
||||
sizes = extracted_content.split("+")
|
||||
size_dict = {}
|
||||
for size in sizes:
|
||||
size_dict[size] = ""
|
||||
return size_dict
|
||||
|
||||
|
||||
|
||||
|
||||
def updateScrollArea(self):
|
||||
|
||||
another_function()
|
||||
|
||||
if not is_coreldraw_running():
|
||||
QMessageBox.warning(self, "警告", "CorelDRAW未运行,无法执行操作。")
|
||||
return
|
||||
|
||||
plt_file_path = self.initial_plt_path
|
||||
|
||||
# 获取DXF文件路径
|
||||
dxf_file_path = self.initial_dxf_path
|
||||
extracted_content = self.extract_content_from_plt_path(plt_file_path)
|
||||
|
||||
if dxf_file_path:
|
||||
# 去掉括号内内容后的PLT文件名作为DXF文件名
|
||||
plt_filename = os.path.basename(plt_file_path)
|
||||
# dxf_filename = re.sub(r'\(.*?\)', '', plt_filename)
|
||||
self.process_dxf_file(dxf_file_path, extracted_content) # 调用解析函数并传入单码片数和新的DXF文件名
|
||||
print("DXF文件解析完成!")
|
||||
else:
|
||||
QMessageBox.warning(self, "警告", "没有选择DXF文件。请先选择一个DXF文件。", QMessageBox.Ok)
|
||||
print()
|
||||
|
||||
|
||||
|
||||
self.run_coreldraw_macros()
|
||||
single_code_pieces = int(self.getSinglePieceCount()) # 获取单码片数
|
||||
print(single_code_pieces)
|
||||
# 打印滚动区域中的输入框内容
|
||||
code_quantities = {} # 创建一个新的字典用于存储数据
|
||||
|
||||
for label, line_edit in self.dxfLineEdits.items():
|
||||
text = line_edit.text()
|
||||
if text.isdigit():
|
||||
value = int(text) # 尝试将文本转换为整数
|
||||
else:
|
||||
try:
|
||||
value = float(text) # 尝试将文本转换为浮点数
|
||||
except ValueError:
|
||||
print(f"Invalid value for {label}: {text}")
|
||||
continue # 转换失败,跳过当前循环迭代
|
||||
|
||||
code_quantities[label] = value # 存储转换后的数字到字典
|
||||
|
||||
print(code_quantities)
|
||||
length = len(code_quantities)
|
||||
print(length) # 输出 3,因为字典中有三对键值对
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def updateLineEditsFromSizes(self):
|
||||
for size_label, line_edit in self.dxfLineEdits.items():
|
||||
self.sizes[size_label] = line_edit.text()
|
||||
|
||||
def populateScrollArea(self):
|
||||
self.clearScrollArea()
|
||||
for size_label, size_text in self.sizes.items():
|
||||
size_layout = QHBoxLayout()
|
||||
size_layout.addWidget(QLabel(size_label))
|
||||
|
||||
line_edit = QLineEdit()
|
||||
line_edit.setValidator(QIntValidator())
|
||||
line_edit.setFixedSize(100, 30)
|
||||
line_edit.setText(size_text)
|
||||
|
||||
self.dxfLineEdits[size_label] = line_edit
|
||||
size_layout.addWidget(line_edit)
|
||||
|
||||
self.scrollWidgetLayout.addLayout(size_layout)
|
||||
|
||||
def clearScrollArea(self):
|
||||
for i in reversed(range(self.scrollWidgetLayout.count())):
|
||||
item = self.scrollWidgetLayout.itemAt(i)
|
||||
if isinstance(item, QHBoxLayout) or isinstance(item, QVBoxLayout):
|
||||
while item.count():
|
||||
widget = item.takeAt(0).widget()
|
||||
if widget:
|
||||
widget.deleteLater()
|
||||
self.dxfLineEdits.clear() # 清空部件引用
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
mainWindow = YourMainWindow9()
|
||||
mainWindow.show()
|
||||
sys.exit(app.exec_())
|
||||
4
temp_backup/Server_redundant/tempdemo/psmark/程序配置.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
[程序配置]
|
||||
ps应用名 = Photoshop.Application
|
||||
@@ -0,0 +1 @@
|
||||
qwe123456
|
||||
177
temp_backup/Server_redundant/tempdemo/serveradmin/server.py
Normal file
@@ -0,0 +1,177 @@
|
||||
from flask import *
|
||||
|
||||
import os
|
||||
import pymysql
|
||||
import tempfile
|
||||
import shutil
|
||||
from zipfile import ZipFile
|
||||
import datetime
|
||||
|
||||
#==================================================================================================
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
ADMIN_USERNAME = "admin"
|
||||
ADMIN_PASSWORD = "fo847543jfrgowjfa8otu43"
|
||||
|
||||
#==================================================================================================
|
||||
def get_connect():
|
||||
host = "rm-bp1s36ps814qp23b7uo.mysql.rds.aliyuncs.com"
|
||||
user = "zw1847930177"
|
||||
password = "Zuowei1216"
|
||||
database = "program"
|
||||
charset = "utf8"
|
||||
port = 3306
|
||||
conn = pymysql.connect(host=host, user=user, password=password, database=database, charset=charset, port=port)
|
||||
return conn
|
||||
|
||||
def getallusers():
|
||||
try:
|
||||
conn = get_connect()
|
||||
cur = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cur.execute(f'select * from user;')
|
||||
user_data = cur.fetchall()
|
||||
return user_data
|
||||
|
||||
finally:
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
def new_users(username, password, code):
|
||||
conn = get_connect()
|
||||
cur = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cur.execute(f"""INSERT INTO `program`.`user` (`username`, `password`, `code`, `expiredate`) VALUES ('{username}', '{password}', '{code}', '{(datetime.datetime.now() + datetime.timedelta(days=14)).strftime("%Y-%m-%d %H:%M:%S")}');""")
|
||||
conn.commit()
|
||||
|
||||
|
||||
#==================================================================================================
|
||||
|
||||
def 无code():
|
||||
userinfo_filepath = os.path.join("tmp", "userinfo.txt")
|
||||
with open(userinfo_filepath, 'w') as f:
|
||||
f.write("error")
|
||||
with ZipFile(os.path.join("tmp", 'result.zip'), 'w') as z:
|
||||
z.write(userinfo_filepath, arcname="userinfo.txt")
|
||||
|
||||
return send_from_directory("tmp", "result.zip", as_attachment=True)
|
||||
|
||||
def 错误的用户名或密码():
|
||||
userinfo_filepath = os.path.join("tmp", "userinfo.txt")
|
||||
with open(userinfo_filepath, 'w') as f:
|
||||
f.write("error")
|
||||
with ZipFile(os.path.join("tmp", 'result.zip'), 'w') as z:
|
||||
z.write(userinfo_filepath, arcname="userinfo.txt")
|
||||
return send_from_directory("tmp", "result.zip", as_attachment=True)
|
||||
|
||||
#==================================================================================================
|
||||
def 返回正常数据(username, password):
|
||||
|
||||
userinfo_filepath = os.path.join("tmp", "userinfo.txt")
|
||||
with open(userinfo_filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(f"{username}\n{password}")
|
||||
|
||||
with open("using.txt", 'r') as f:
|
||||
shutil.copyfile(f"archives/{f.read()}.zip", os.path.join("tmp", "data.zip"))
|
||||
|
||||
with ZipFile(os.path.join("tmp", 'result.zip'), 'w') as z:
|
||||
z.write(userinfo_filepath, arcname="userinfo.txt")
|
||||
z.write(os.path.join("tmp", "data.zip"), arcname="data.zip")
|
||||
|
||||
return send_from_directory("tmp", "result.zip", as_attachment=True)
|
||||
|
||||
|
||||
#==================================================================================================
|
||||
@app.route("/query", methods=["POST"])
|
||||
def query():
|
||||
username = request.args.get("username", "")
|
||||
password = request.args.get("password", "")
|
||||
code = request.args.get("code", "")
|
||||
|
||||
allusers = getallusers()
|
||||
|
||||
if code == "":
|
||||
return 无code()
|
||||
|
||||
if username == "" and password == "":
|
||||
for user in allusers:
|
||||
if code == user["code"] and (user["expiredate"] - datetime.datetime.now()).total_seconds() > 0:
|
||||
return 返回正常数据(user["username"], user["password"])
|
||||
else:
|
||||
for user in allusers:
|
||||
if username == user["username"] and password == user["password"] and code == user["code"] and (user["expiredate"] - datetime.datetime.now()).total_seconds() > 0:
|
||||
return 返回正常数据(user["username"], user["password"])
|
||||
|
||||
return 错误的用户名或密码()
|
||||
|
||||
#==================================================================================================
|
||||
# 设置使用的档案
|
||||
@app.route("/set_using_archives", methods=["POST"])
|
||||
def set_using_archives():
|
||||
# 检查权限
|
||||
username = request.form.get("username", "")
|
||||
password = request.form.get("password", "")
|
||||
|
||||
if not (username == ADMIN_USERNAME and password == ADMIN_PASSWORD):
|
||||
abort(403)
|
||||
|
||||
result = request.form.get("result")
|
||||
|
||||
if result not in [os.path.basename(a).split('.')[0] for a in os.listdir("archives")]:
|
||||
return 'error'
|
||||
|
||||
with open("using.txt", 'w') as f:
|
||||
f.write(result)
|
||||
|
||||
return 'OK'
|
||||
#==================================================================================================
|
||||
# 获取正在使用的档案名称
|
||||
@app.route("/get_using_archives_name", methods=["GET"])
|
||||
def get_using_archives_name():
|
||||
with open("using.txt", 'r') as f:
|
||||
return f.read()
|
||||
#==================================================================================================
|
||||
# 注册
|
||||
@app.route("/register", methods=["POST"])
|
||||
def register():
|
||||
if request.method == "POST":
|
||||
username = request.form.get("username")
|
||||
password = request.form.get("password")
|
||||
code = request.form.get("code")
|
||||
adminpassword = request.form.get("adminpassword")
|
||||
|
||||
with open("adminpassword.txt", 'r') as f:
|
||||
true_adminpassword = f.read().strip()
|
||||
|
||||
if adminpassword != true_adminpassword:
|
||||
return "error"
|
||||
|
||||
else:
|
||||
new_users(username, password, code)
|
||||
return 'success'
|
||||
|
||||
#==================================================================================================
|
||||
@app.route("/archives", methods=["GET", "POST"])
|
||||
def archives():
|
||||
if request.method == "GET":
|
||||
# 获取档案列表
|
||||
archives = os.listdir("archives")
|
||||
archives = [os.path.basename(a).split(".")[0] for a in archives]
|
||||
return jsonify(archives)
|
||||
|
||||
elif request.method == "POST":
|
||||
# 上传档案
|
||||
username = request.form.get("username")
|
||||
password = request.form.get("password")
|
||||
|
||||
if not (username == ADMIN_USERNAME and password == ADMIN_PASSWORD):
|
||||
abort(403)
|
||||
|
||||
file = request.files['file']
|
||||
file.save(f"./archives/{str(datetime.datetime.now()).split('.')[0].replace(':', '')}.zip")
|
||||
|
||||
return 'OK'
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5001, debug=True)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
2023-09-09 010039
|
||||
11
temp_backup/Server_redundant/tempdemo/update_version.py.bak
Normal file
@@ -0,0 +1,11 @@
|
||||
import sqlite3
|
||||
conn = sqlite3.connect('designercep.db')
|
||||
|
||||
# Update plugin_groups table - column is current_version_file not version
|
||||
conn.execute("UPDATE plugin_groups SET current_version_file = 'core-v1.3.0.zip' WHERE id=1")
|
||||
conn.commit()
|
||||
print('Updated Default group to core-v1.0.5.zip')
|
||||
|
||||
cursor = conn.execute('SELECT * FROM plugin_groups')
|
||||
print(list(cursor))
|
||||
conn.close()
|
||||
113
temp_backup/Server_redundant/tempdocs/API_DOCUMENTATION.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# DesignerCEP 认证模块接口文档
|
||||
|
||||
本文档描述了后端新增的邮箱注册验证与密码重置相关接口。
|
||||
|
||||
## 1. 注册 (Register)
|
||||
|
||||
支持传入邮箱进行注册。如果传入邮箱,系统将发送验证码邮件。
|
||||
|
||||
- **URL**: `/api/v1/auth/register`
|
||||
- **Method**: `POST`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"password": "password123",
|
||||
"confirm_password": "password123",
|
||||
"email": "user1@gmail.com", // [新增] 可选,推荐填写
|
||||
"device_id": "device_unique_id"
|
||||
}
|
||||
```
|
||||
- **Response**:
|
||||
- Success (200):
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsIn...",
|
||||
"token_type": "bearer",
|
||||
"username": "user1"
|
||||
}
|
||||
```
|
||||
- Error (400): 用户名已存在 / 邮箱已存在 / 密码不一致
|
||||
|
||||
## 2. 验证邮箱 (Verify Email)
|
||||
|
||||
用户收到验证码邮件后,在前端输入验证码进行验证。
|
||||
|
||||
- **URL**: `/api/v1/auth/verify-email`
|
||||
- **Method**: `POST`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"code": "123456" // 邮件中的6位数字验证码
|
||||
}
|
||||
```
|
||||
- **Response**:
|
||||
- Success (200):
|
||||
```json
|
||||
{
|
||||
"detail": "验证成功"
|
||||
}
|
||||
```
|
||||
- Error (400): 验证码错误
|
||||
|
||||
## 3. 忘记密码 (Forgot Password)
|
||||
|
||||
用户输入注册邮箱,请求重置密码。
|
||||
|
||||
- **URL**: `/api/v1/auth/forgot-password`
|
||||
- **Method**: `POST`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"email": "user1@gmail.com"
|
||||
}
|
||||
```
|
||||
- **Response**:
|
||||
- Success (200):
|
||||
```json
|
||||
{
|
||||
"detail": "如果邮箱存在,重置邮件已发送"
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 重置密码 (Reset Password)
|
||||
|
||||
用户点击邮件中的链接(或手动输入Token),设置新密码。
|
||||
|
||||
- **URL**: `/api/v1/auth/reset-password`
|
||||
- **Method**: `POST`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"token": "reset_token_from_email",
|
||||
"new_password": "new_password123",
|
||||
"confirm_password": "new_password123"
|
||||
}
|
||||
```
|
||||
- **Response**:
|
||||
- Success (200):
|
||||
```json
|
||||
{
|
||||
"detail": "密码重置成功"
|
||||
}
|
||||
```
|
||||
- Error (400): Token 无效 / Token 已过期 / 密码不一致
|
||||
|
||||
## 流程说明
|
||||
|
||||
1. **注册流程**:
|
||||
- 用户填写注册信息(含邮箱)。
|
||||
- 提交 `/register`。
|
||||
- 如果成功,后端自动登录并返回 Token。
|
||||
- 如果填写了邮箱,后端会发送一封验证邮件。
|
||||
- 前端提示用户查收邮件并输入验证码。
|
||||
- 用户输入验证码,前端调用 `/verify-email`。
|
||||
|
||||
2. **找回密码流程**:
|
||||
- 用户点击“忘记密码”。
|
||||
- 输入邮箱,前端调用 `/forgot-password`。
|
||||
- 用户收到邮件,包含重置 Token。
|
||||
- 前端提供重置密码界面,用户输入新密码。
|
||||
- 前端调用 `/reset-password`(带上 Token 和新密码)。
|
||||
- 重置成功后,跳转至登录页。
|
||||
339
temp_backup/Server_redundant/tempdocs/API密钥使用指南.md
Normal file
@@ -0,0 +1,339 @@
|
||||
# API Key 使用指南
|
||||
|
||||
## ✅ 已启用 API Key 验证
|
||||
|
||||
现在所有对 `/api/v1/jsx_demo/calculate` 的请求都需要提供有效的 API Key。
|
||||
|
||||
---
|
||||
|
||||
## 🔑 当前可用的 API Keys
|
||||
|
||||
### 1. 测试密钥(开发使用)
|
||||
```
|
||||
API Key: demo_key_123
|
||||
名称: 测试密钥
|
||||
权限: calculate
|
||||
限制: 100次/小时
|
||||
```
|
||||
|
||||
### 2. 生产密钥(生产环境)
|
||||
```
|
||||
API Key: prod_key_xyz789abc
|
||||
名称: 生产密钥
|
||||
权限: calculate, admin
|
||||
限制: 1000次/小时
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 日志示例
|
||||
|
||||
启用 API Key 后,后端日志会显示:
|
||||
|
||||
```
|
||||
============================================================
|
||||
📥 收到计算请求
|
||||
时间: 2024-12-16 15:30:45
|
||||
表达式: 87-98
|
||||
API Key: demo_key_123
|
||||
============================================================
|
||||
✅ API Key 验证通过 | 名称: 测试密钥 | 权限: ['calculate']
|
||||
🛡️ 安全检查: 验证表达式格式...
|
||||
✅ 表达式格式验证通过
|
||||
🔒 开始执行核心算法...
|
||||
✅ 计算完成: 87-98 = -11
|
||||
============================================================
|
||||
```
|
||||
|
||||
### 如果 API Key 无效:
|
||||
|
||||
```
|
||||
============================================================
|
||||
📥 收到计算请求
|
||||
时间: 2024-12-16 15:30:45
|
||||
表达式: 87-98
|
||||
API Key: invalid_key_xxx
|
||||
============================================================
|
||||
❌ API Key 验证失败: invalid_key_xxx
|
||||
```
|
||||
|
||||
**前端会收到:** `403 Forbidden: 无效的 API Key`
|
||||
|
||||
---
|
||||
|
||||
## 🔧 管理 API Keys
|
||||
|
||||
### 查看所有 Keys
|
||||
|
||||
打开 `Server/app/core/api_keys.py`:
|
||||
|
||||
```python
|
||||
VALID_KEYS: Dict[str, dict] = {
|
||||
"demo_key_123": {
|
||||
"name": "测试密钥",
|
||||
"created": "2024-12-16",
|
||||
"permissions": ["calculate"],
|
||||
"rate_limit": 100
|
||||
},
|
||||
# ... 更多 keys
|
||||
}
|
||||
```
|
||||
|
||||
### 添加新的 API Key
|
||||
|
||||
**方法 1:直接编辑配置文件**
|
||||
|
||||
在 `api_keys.py` 中添加:
|
||||
|
||||
```python
|
||||
"your_new_key_456": {
|
||||
"name": "客户A的密钥",
|
||||
"created": "2024-12-16",
|
||||
"permissions": ["calculate"],
|
||||
"rate_limit": 200
|
||||
}
|
||||
```
|
||||
|
||||
**方法 2:使用 Python 代码**
|
||||
|
||||
```python
|
||||
from app.core.api_keys import APIKeyManager
|
||||
|
||||
# 添加新 Key
|
||||
APIKeyManager.add_key(
|
||||
api_key="customer_key_789",
|
||||
name="客户B的密钥",
|
||||
permissions=["calculate", "export"]
|
||||
)
|
||||
```
|
||||
|
||||
### 删除 API Key
|
||||
|
||||
```python
|
||||
from app.core.api_keys import APIKeyManager
|
||||
|
||||
# 删除 Key
|
||||
APIKeyManager.remove_key("old_key_123")
|
||||
```
|
||||
|
||||
### 检查权限
|
||||
|
||||
```python
|
||||
from app.core.api_keys import APIKeyManager
|
||||
|
||||
# 检查是否有权限
|
||||
has_permission = APIKeyManager.check_permission("demo_key_123", "calculate")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 前端配置
|
||||
|
||||
### 当前配置(已设置)
|
||||
|
||||
`Designer/src/api/jsxApi/inline/hybrid-demo.ts`:
|
||||
|
||||
```typescript
|
||||
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/calculate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': 'demo_key_123' // 🔐 使用测试密钥
|
||||
},
|
||||
body: JSON.stringify({
|
||||
expression: layerName
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### 切换到生产密钥
|
||||
|
||||
修改 API Key:
|
||||
|
||||
```typescript
|
||||
'X-API-Key': 'prod_key_xyz789abc' // 使用生产密钥
|
||||
```
|
||||
|
||||
### 从配置文件读取(推荐)
|
||||
|
||||
创建 `Designer/src/config/apiKeys.ts`:
|
||||
|
||||
```typescript
|
||||
export const API_KEYS = {
|
||||
development: 'demo_key_123',
|
||||
production: 'prod_key_xyz789abc'
|
||||
};
|
||||
|
||||
// 根据环境自动选择
|
||||
export const getCurrentApiKey = () => {
|
||||
return import.meta.env.DEV
|
||||
? API_KEYS.development
|
||||
: API_KEYS.production;
|
||||
};
|
||||
```
|
||||
|
||||
然后在 `hybrid-demo.ts` 中使用:
|
||||
|
||||
```typescript
|
||||
import { getCurrentApiKey } from '@/config/apiKeys';
|
||||
|
||||
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/calculate`, {
|
||||
headers: {
|
||||
'X-API-Key': getCurrentApiKey() // 自动选择正确的 Key
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试 API Key 验证
|
||||
|
||||
### 测试 1:使用有效的 Key
|
||||
|
||||
1. 创建图层名称:`87-98`
|
||||
2. 点击"智能配色"
|
||||
3. **预期结果:** ✅ 成功计算
|
||||
|
||||
**后端日志:**
|
||||
```
|
||||
✅ API Key 验证通过 | 名称: 测试密钥
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 测试 2:使用无效的 Key
|
||||
|
||||
临时修改前端,使用错误的 Key:
|
||||
|
||||
```typescript
|
||||
'X-API-Key': 'wrong_key_xxx'
|
||||
```
|
||||
|
||||
**预期结果:** ❌ 403 错误
|
||||
|
||||
**后端日志:**
|
||||
```
|
||||
❌ API Key 验证失败: wrong_key_xxx
|
||||
```
|
||||
|
||||
**前端错误:**
|
||||
```
|
||||
Message.error('无效的 API Key')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 测试 3:不提供 Key
|
||||
|
||||
注释掉 API Key:
|
||||
|
||||
```typescript
|
||||
// 'X-API-Key': 'demo_key_123'
|
||||
```
|
||||
|
||||
**预期结果:** ❌ 403 错误
|
||||
|
||||
**后端日志:**
|
||||
```
|
||||
❌ API Key 验证失败: None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 安全最佳实践
|
||||
|
||||
### ✅ 应该做的
|
||||
|
||||
1. **不同环境使用不同的 Key**
|
||||
- 开发环境:`demo_key_123`
|
||||
- 生产环境:`prod_key_xyz789abc`
|
||||
|
||||
2. **定期更换 Key**
|
||||
```python
|
||||
# 每季度更新一次
|
||||
"new_key_2024_q1": {...}
|
||||
```
|
||||
|
||||
3. **使用环境变量**
|
||||
```python
|
||||
import os
|
||||
API_KEY = os.getenv('DESIGNER_API_KEY', 'demo_key_123')
|
||||
```
|
||||
|
||||
4. **记录所有请求**
|
||||
- 已实现:所有请求都会记录 API Key
|
||||
|
||||
5. **限制调用频率**
|
||||
- 已配置:每个 Key 都有 rate_limit
|
||||
|
||||
### ❌ 不应该做的
|
||||
|
||||
1. ❌ 把 Key 硬编码在公开的代码中
|
||||
2. ❌ 在 Git 中提交包含生产 Key 的文件
|
||||
3. ❌ 多个客户共用同一个 Key
|
||||
4. ❌ 永远不更换 Key
|
||||
|
||||
---
|
||||
|
||||
## 🚀 进一步加强
|
||||
|
||||
### 1. 数据库存储 API Keys
|
||||
|
||||
```python
|
||||
# models/api_key.py
|
||||
class APIKey(Base):
|
||||
__tablename__ = "api_keys"
|
||||
|
||||
key = Column(String, primary_key=True)
|
||||
name = Column(String)
|
||||
permissions = Column(JSON)
|
||||
rate_limit = Column(Integer)
|
||||
created_at = Column(DateTime)
|
||||
expires_at = Column(DateTime)
|
||||
is_active = Column(Boolean, default=True)
|
||||
```
|
||||
|
||||
### 2. Key 过期时间
|
||||
|
||||
```python
|
||||
"demo_key_123": {
|
||||
"expires": "2025-12-31", # Key 会过期
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 使用量统计
|
||||
|
||||
```python
|
||||
"demo_key_123": {
|
||||
"usage_count": 0,
|
||||
"last_used": None,
|
||||
}
|
||||
```
|
||||
|
||||
### 4. IP 绑定
|
||||
|
||||
```python
|
||||
"demo_key_123": {
|
||||
"allowed_ips": ["192.168.1.100", "127.0.0.1"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 总结
|
||||
|
||||
| 功能 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| API Key 验证 | ✅ 已启用 | 所有请求必须提供有效 Key |
|
||||
| 多 Key 支持 | ✅ 已实现 | 可配置多个不同权限的 Key |
|
||||
| 权限控制 | ✅ 已实现 | 每个 Key 可配置不同权限 |
|
||||
| 日志记录 | ✅ 已实现 | 记录所有 Key 使用情况 |
|
||||
| Key 管理器 | ✅ 已实现 | 提供增删改查 API |
|
||||
| 限流配置 | 🔧 已配置 | 待实现实际限流逻辑 |
|
||||
| 数据库存储 | ⚠️ 未实现 | 当前使用配置文件 |
|
||||
| Key 过期 | ⚠️ 未实现 | 可以扩展 |
|
||||
|
||||
---
|
||||
|
||||
**当前 API Key 已启用!所有请求都会被验证和记录!**
|
||||
|
||||
397
temp_backup/Server_redundant/tempdocs/AdminTool配置管理开发文档.md
Normal file
@@ -0,0 +1,397 @@
|
||||
# AdminTool 系统配置管理开发文档
|
||||
|
||||
## 📋 开发需求
|
||||
|
||||
AdminTool需要集成系统配置管理功能,用于可视化管理:
|
||||
1. 功能配置(积分价格、启用/禁用)
|
||||
2. VIP配置(价格、配额、倍数)
|
||||
3. 签到配置(连续天数奖励)
|
||||
4. 数据统计(今日统计、功能使用排行)
|
||||
|
||||
---
|
||||
|
||||
## 🎨 技术方案
|
||||
|
||||
### 方案A:使用 qfluentwidgets(推荐)
|
||||
|
||||
#### 1. 安装依赖
|
||||
|
||||
```bash
|
||||
pip install PyQt-Fluent-Widgets
|
||||
```
|
||||
|
||||
#### 2. 改造现有代码
|
||||
|
||||
**主窗口改造** (`admin_gui.py`):
|
||||
```python
|
||||
from qfluentwidgets import FluentWindow, FluentIcon, NavigationItemPosition
|
||||
|
||||
class AdminWindow(FluentWindow): # 改为继承 FluentWindow
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 启用 Mica 效果
|
||||
self.setMicaEffectEnabled(True)
|
||||
|
||||
# 添加配置管理页面
|
||||
self.config_interface = ConfigInterface(self.api_client, self)
|
||||
|
||||
self.addSubInterface(
|
||||
self.config_interface,
|
||||
icon=FluentIcon.SETTING,
|
||||
text="系统配置",
|
||||
position=NavigationItemPosition.BOTTOM
|
||||
)
|
||||
```
|
||||
|
||||
**配置管理界面** (`config_interface.py`,新建):
|
||||
```python
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout
|
||||
from qfluentwidgets import (
|
||||
Pivot, PushButton, TableWidget, CardWidget,
|
||||
MessageBox, InfoBar
|
||||
)
|
||||
|
||||
class ConfigInterface(QWidget):
|
||||
"""配置管理主界面"""
|
||||
|
||||
def __init__(self, api_client, parent=None):
|
||||
super().__init__(parent)
|
||||
self.api_client = api_client
|
||||
|
||||
# 创建Pivot导航
|
||||
self.pivot = Pivot(self)
|
||||
|
||||
# 添加子页面
|
||||
self.features_tab = FeaturesConfigTab(api_client)
|
||||
self.vip_tab = VIPConfigTab(api_client)
|
||||
self.checkin_tab = CheckInConfigTab(api_client)
|
||||
self.stats_tab = StatsTab(api_client)
|
||||
|
||||
# 添加到Pivot
|
||||
self.pivot.addItem('features', '功能配置', self.features_tab)
|
||||
self.pivot.addItem('vip', 'VIP配置', self.vip_tab)
|
||||
self.pivot.addItem('checkin', '签到配置', self.checkin_tab)
|
||||
self.pivot.addItem('stats', '数据统计', self.stats_tab)
|
||||
|
||||
|
||||
class FeaturesConfigTab(QWidget):
|
||||
"""功能配置标签页"""
|
||||
|
||||
def __init__(self, api_client, parent=None):
|
||||
super().__init__(parent)
|
||||
self.api_client = api_client
|
||||
|
||||
# 表格
|
||||
self.table = TableWidget(self)
|
||||
self.table.setColumnCount(6)
|
||||
self.table.setHorizontalHeaderLabels([
|
||||
"功能名称", "普通价格", "VIP价格", "SVIP价格", "状态", "操作"
|
||||
])
|
||||
|
||||
# 按钮
|
||||
self.btn_add = PushButton("新增功能", self)
|
||||
self.btn_refresh = PushButton("刷新", self)
|
||||
|
||||
self.load_data()
|
||||
|
||||
def load_data(self):
|
||||
"""从API加载数据"""
|
||||
try:
|
||||
resp = requests.get(
|
||||
f"{self.api_client.base_url}/admin/config/features",
|
||||
headers=self.api_client.get_headers()
|
||||
)
|
||||
data = resp.json()
|
||||
# 填充表格...
|
||||
InfoBar.success("加载成功", f"已加载 {len(data)} 个功能")
|
||||
except Exception as e:
|
||||
MessageBox("错误", f"加载失败: {e}", self).exec()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方案B:使用现有PyQt5(快速实现)
|
||||
|
||||
如果不想安装新依赖,可以在现有 `admin_gui.py` 基础上:
|
||||
|
||||
#### 1. 在 `setup_` 中添加新标签页
|
||||
|
||||
```python
|
||||
def __init__(self):
|
||||
# ... 现有代码 ...
|
||||
|
||||
# 新增第4个标签页
|
||||
self.config_tab = QWidget()
|
||||
self.setup_config_tab()
|
||||
self.tabs.addTab(self.config_tab, "系统配置")
|
||||
|
||||
def setup_config_tab(self):
|
||||
"""设置系统配置标签页"""
|
||||
layout = QVBoxLayout(self.config_tab)
|
||||
|
||||
# 子标签页
|
||||
sub_tabs = QTabWidget()
|
||||
|
||||
# 功能配置
|
||||
features_widget = QWidget()
|
||||
self.setup_features_config(features_widget)
|
||||
sub_tabs.addTab(features_widget, "功能配置")
|
||||
|
||||
# VIP配置
|
||||
vip_widget = QWidget()
|
||||
self.setup_vip_config(vip_widget)
|
||||
sub_tabs.addTab(vip_widget, "VIP配置")
|
||||
|
||||
# 签到配置
|
||||
checkin_widget = QWidget()
|
||||
self.setup_checkin_config(checkin_widget)
|
||||
sub_tabs.addTab(checkin_widget, "签到配置")
|
||||
|
||||
# 统计
|
||||
stats_widget = QWidget()
|
||||
self.setup_stats(stats_widget)
|
||||
sub_tabs.addTab(stats_widget, "数据统计")
|
||||
|
||||
layout.addWidget(sub_tabs)
|
||||
```
|
||||
|
||||
#### 2. 实现各个子界面
|
||||
|
||||
```python
|
||||
def setup_features_config(self, widget):
|
||||
"""功能配置界面"""
|
||||
layout = QVBoxLayout(widget)
|
||||
|
||||
# 操作按钮
|
||||
btn_layout = QHBoxLayout()
|
||||
btn_add = QPushButton("新增功能")
|
||||
btn_add.clicked.connect(self.add_feature_config)
|
||||
btn_refresh = QPushButton("刷新")
|
||||
btn_refresh.clicked.connect(self.refresh_features_config)
|
||||
btn_layout.addWidget(btn_add)
|
||||
btn_layout.addWidget(btn_refresh)
|
||||
btn_layout.addStretch()
|
||||
layout.addLayout(btn_layout)
|
||||
|
||||
# 表格
|
||||
self.features_table = QTableWidget()
|
||||
self.features_table.setColumnCount(7)
|
||||
self.features_table.setHorizontalHeaderLabels([
|
||||
"功能名称", "分类", "普通价格", "VIP价格", "SVIP价格", "状态", "操作"
|
||||
])
|
||||
self.features_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
layout.addWidget(self.features_table)
|
||||
|
||||
# 加载数据
|
||||
self.refresh_features_config()
|
||||
|
||||
def refresh_features_config(self):
|
||||
"""刷新功能配置"""
|
||||
try:
|
||||
resp = requests.get(
|
||||
f"{self.api_client.base_url}/admin/config/features",
|
||||
headers=self.api_client.get_headers()
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
self.features_table.setRowCount(len(data))
|
||||
for i, feature in enumerate(data):
|
||||
self.features_table.setItem(i, 0, QTableWidgetItem(feature['feature_name']))
|
||||
self.features_table.setItem(i, 1, QTableWidgetItem(feature['category']))
|
||||
self.features_table.setItem(i, 2, QTableWidgetItem(str(feature['points_cost'])))
|
||||
self.features_table.setItem(i, 3, QTableWidgetItem(str(feature['vip_points_cost'])))
|
||||
self.features_table.setItem(i, 4, QTableWidgetItem(str(feature['svip_points_cost'])))
|
||||
self.features_table.setItem(i, 5, QTableWidgetItem("✓" if feature['enabled'] else "✗"))
|
||||
|
||||
# 操作按钮
|
||||
btn_edit = QPushButton("编辑")
|
||||
btn_edit.clicked.connect(lambda checked, f=feature: self.edit_feature_config(f))
|
||||
self.features_table.setCellWidget(i, 6, btn_edit)
|
||||
|
||||
QMessageBox.information(self, "成功", f"已加载 {len(data)} 个功能配置")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"加载失败: {e}")
|
||||
|
||||
def edit_feature_config(self, feature):
|
||||
"""编辑功能配置"""
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle(f"编辑功能: {feature['feature_name']}")
|
||||
layout = QFormLayout(dialog)
|
||||
|
||||
# 输入框
|
||||
points_input = QSpinBox()
|
||||
points_input.setRange(0, 9999)
|
||||
points_input.setValue(feature['points_cost'])
|
||||
layout.addRow("普通用户积分:", points_input)
|
||||
|
||||
vip_points_input = QSpinBox()
|
||||
vip_points_input.setRange(0, 9999)
|
||||
vip_points_input.setValue(feature['vip_points_cost'])
|
||||
layout.addRow("VIP积分:", vip_points_input)
|
||||
|
||||
svip_points_input = QSpinBox()
|
||||
svip_points_input.setRange(0, 9999)
|
||||
svip_points_input.setValue(feature['svip_points_cost'])
|
||||
layout.addRow("SVIP积分:", svip_points_input)
|
||||
|
||||
enabled_check = QCheckBox()
|
||||
enabled_check.setChecked(feature['enabled'])
|
||||
layout.addRow("启用:", enabled_check)
|
||||
|
||||
# 按钮
|
||||
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
buttons.accepted.connect(dialog.accept)
|
||||
buttons.rejected.connect(dialog.reject)
|
||||
layout.addRow(buttons)
|
||||
|
||||
if dialog.exec() == QDialog.Accepted:
|
||||
# 提交更新
|
||||
try:
|
||||
resp = requests.put(
|
||||
f"{self.api_client.base_url}/admin/config/features/{feature['feature_key']}",
|
||||
json={
|
||||
"points_cost": points_input.value(),
|
||||
"vip_points_cost": vip_points_input.value(),
|
||||
"svip_points_cost": svip_points_input.value(),
|
||||
"enabled": enabled_check.isChecked()
|
||||
},
|
||||
headers=self.api_client.get_headers()
|
||||
)
|
||||
resp.raise_for_status()
|
||||
QMessageBox.information(self, "成功", "更新成功")
|
||||
self.refresh_features_config()
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"更新失败: {e}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 API接口参考
|
||||
|
||||
### 功能配置API
|
||||
|
||||
```python
|
||||
# 获取所有功能
|
||||
GET /api/v1/admin/config/features
|
||||
Headers: x-admin-token: admin-secret-token
|
||||
|
||||
# 新增功能
|
||||
POST /api/v1/admin/config/features
|
||||
Body: {
|
||||
"feature_key": "new_feature",
|
||||
"feature_name": "新功能",
|
||||
"category": "ai",
|
||||
"points_cost": 60,
|
||||
"vip_points_cost": 0,
|
||||
"svip_points_cost": 0
|
||||
}
|
||||
|
||||
# 更新功能
|
||||
PUT /api/v1/admin/config/features/{feature_key}
|
||||
Body: {
|
||||
"points_cost": 80,
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### VIP配置API
|
||||
|
||||
```python
|
||||
# 获取VIP配置
|
||||
GET /api/v1/admin/config/vip
|
||||
|
||||
# 更新VIP配置
|
||||
PUT /api/v1/admin/config/vip/vip
|
||||
Body: {
|
||||
"price": 35.00,
|
||||
"daily_quota": 25,
|
||||
"points_multiplier": 1.60
|
||||
}
|
||||
```
|
||||
|
||||
### 签到配置API
|
||||
|
||||
```python
|
||||
# 获取签到配置
|
||||
GET /api/v1/admin/config/checkin
|
||||
|
||||
# 新增档位
|
||||
POST /api/v1/admin/config/checkin
|
||||
Body: {
|
||||
"consecutive_days": 60,
|
||||
"base_points": 10,
|
||||
"bonus_points": 150,
|
||||
"total_points": 160
|
||||
}
|
||||
|
||||
# 更新档位
|
||||
PUT /api/v1/admin/config/checkin/7
|
||||
Body: {
|
||||
"bonus_points": 25,
|
||||
"total_points": 35
|
||||
}
|
||||
```
|
||||
|
||||
### 统计API
|
||||
|
||||
```python
|
||||
# 今日统计
|
||||
GET /api/v1/admin/stats/today
|
||||
|
||||
# 功能使用排行
|
||||
GET /api/v1/admin/stats/feature-usage?days=7
|
||||
|
||||
# 积分趋势
|
||||
GET /api/v1/admin/stats/points-trend?days=7
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 开发检查清单
|
||||
|
||||
- [ ] 安装依赖(如使用方案A)
|
||||
- [ ] 改造主窗口类
|
||||
- [ ] 创建配置管理界面
|
||||
- [ ] 实现功能配置CRUD
|
||||
- [ ] 实现VIP配置编辑
|
||||
- [ ] 实现签到配置编辑
|
||||
- [ ] 实现数据统计展示
|
||||
- [ ] 连接后端API
|
||||
- [ ] 错误处理和提示
|
||||
- [ ] 测试所有功能
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **权限验证**: 所有管理接口都需要 `x-admin-token: admin-secret-token`
|
||||
2. **错误处理**: 使用 try-except 捕获网络异常
|
||||
3. **用户友好**: 操作前确认,操作后提示
|
||||
4. **数据刷新**: 修改后自动刷新列表
|
||||
5. **布局美观**: 合理使用Grid/Box布局
|
||||
|
||||
---
|
||||
|
||||
## 🎯 优先级
|
||||
|
||||
### P0(必须)
|
||||
- 功能配置查看和编辑
|
||||
- VIP配置编辑
|
||||
- 签到配置编辑
|
||||
|
||||
### P1(重要)
|
||||
- 功能配置新增/删除
|
||||
- 签到配置新增/删除
|
||||
- 数据统计展示
|
||||
|
||||
### P2(可选)
|
||||
- Fluent Design 风格改造
|
||||
- 图表可视化
|
||||
- 高级筛选和搜索
|
||||
|
||||
---
|
||||
|
||||
**开发愉快!如有问题请参考主文档或API文档。**
|
||||
|
||||
820
temp_backup/Server_redundant/tempdocs/Caddy部署指南.md
Normal file
@@ -0,0 +1,820 @@
|
||||
# 🚀 DesignerCEP Caddy 部署指南
|
||||
|
||||
## 为什么选择 Caddy?
|
||||
|
||||
✅ **自动 HTTPS**:无需手动申请证书,自动从 Let's Encrypt 获取并续期
|
||||
✅ **配置简单**:比 Nginx 简单 10 倍,一看就懂
|
||||
✅ **开箱即用**:自动处理 HTTP/2, GZIP 压缩
|
||||
✅ **完美支持 Cloudflare**:自动识别并配置
|
||||
|
||||
---
|
||||
|
||||
## 📦 1. 安装 Caddy
|
||||
|
||||
### Ubuntu/Debian
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
|
||||
|
||||
# 添加 Caddy 官方仓库
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
|
||||
# 更新并安装
|
||||
sudo apt update
|
||||
sudo apt install caddy
|
||||
|
||||
# 验证安装
|
||||
caddy version
|
||||
```
|
||||
|
||||
### CentOS/RHEL
|
||||
|
||||
```bash
|
||||
# 添加 Caddy 官方仓库
|
||||
dnf install 'dnf-command(copr)'
|
||||
dnf copr enable @caddy/caddy
|
||||
dnf install caddy
|
||||
|
||||
# 启动服务
|
||||
sudo systemctl enable caddy
|
||||
sudo systemctl start caddy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 2. 准备项目目录
|
||||
|
||||
```bash
|
||||
# 创建项目目录
|
||||
sudo mkdir -p /var/www/DesignerCEP/Server/static/{shell,core,downloads}
|
||||
|
||||
# 设置权限
|
||||
sudo chown -R $USER:$USER /var/www/DesignerCEP
|
||||
chmod -R 755 /var/www/DesignerCEP
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 3. 配置 Caddy
|
||||
|
||||
### 方案 A: 标准部署(Let's Encrypt 自动证书)
|
||||
|
||||
**适用于**:独立域名,不使用 Cloudflare 代理
|
||||
|
||||
```bash
|
||||
# 创建 Caddyfile
|
||||
sudo nano /etc/caddy/Caddyfile
|
||||
```
|
||||
|
||||
```caddy
|
||||
# /etc/caddy/Caddyfile
|
||||
|
||||
# ========== 主站配置 ==========
|
||||
your-domain.com, www.your-domain.com {
|
||||
# ✅ Caddy 会自动处理 HTTPS 证书
|
||||
|
||||
# ========== API 请求 → FastAPI ==========
|
||||
handle /api/* {
|
||||
reverse_proxy localhost:8000 {
|
||||
# 传递真实 IP
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
# ========== Shell 在线登录页 ==========
|
||||
handle /shell/* {
|
||||
root * /var/www/DesignerCEP/Server/static/shell
|
||||
try_files {path} {path}/ /shell/index.html
|
||||
file_server
|
||||
|
||||
# HTML 不缓存
|
||||
@html {
|
||||
path *.html
|
||||
}
|
||||
header @html Cache-Control "no-cache, no-store, must-revalidate"
|
||||
|
||||
# JS/CSS 长期缓存
|
||||
@assets {
|
||||
path *.js *.css
|
||||
}
|
||||
header @assets Cache-Control "public, max-age=31536000, immutable"
|
||||
}
|
||||
|
||||
# ========== Core 核心应用 ==========
|
||||
handle /core/* {
|
||||
root * /var/www/DesignerCEP/Server/static/core
|
||||
file_server
|
||||
|
||||
# HTML 不缓存
|
||||
@html {
|
||||
path *.html
|
||||
}
|
||||
header @html Cache-Control "no-cache, no-store, must-revalidate"
|
||||
|
||||
# JS/CSS 长期缓存
|
||||
@assets {
|
||||
path *.js *.css
|
||||
}
|
||||
header @assets Cache-Control "public, max-age=31536000, immutable"
|
||||
}
|
||||
|
||||
# ========== 下载文件 ==========
|
||||
handle /downloads/* {
|
||||
root * /var/www/DesignerCEP/Server/static/downloads
|
||||
file_server
|
||||
|
||||
# 启用断点续传
|
||||
header Accept-Ranges bytes
|
||||
|
||||
# 缓存 1 天
|
||||
header Cache-Control "public, max-age=86400"
|
||||
}
|
||||
|
||||
# ========== 根路径重定向 ==========
|
||||
handle / {
|
||||
redir /shell/ permanent
|
||||
}
|
||||
|
||||
# ========== 通用配置 ==========
|
||||
# 自动 GZIP 压缩
|
||||
encode gzip zstd
|
||||
|
||||
# 安全头
|
||||
header {
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
-Server # 隐藏服务器信息
|
||||
}
|
||||
|
||||
# 日志
|
||||
log {
|
||||
output file /var/log/caddy/designer-cep.log {
|
||||
roll_size 100mb
|
||||
roll_keep 10
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方案 B: Cloudflare 代理部署(推荐)
|
||||
|
||||
**适用于**:域名使用 Cloudflare 橙云代理
|
||||
|
||||
```bash
|
||||
sudo nano /etc/caddy/Caddyfile
|
||||
```
|
||||
|
||||
```caddy
|
||||
# /etc/caddy/Caddyfile (Cloudflare 版本)
|
||||
{
|
||||
# ✅ 关闭自动 HTTPS(Cloudflare 已经提供)
|
||||
auto_https off
|
||||
|
||||
# 或者使用内部证书
|
||||
# auto_https disable_redirects
|
||||
}
|
||||
|
||||
# ========== HTTP 配置(Cloudflare 会加 HTTPS)==========
|
||||
http://your-domain.com, http://www.your-domain.com {
|
||||
|
||||
# ========== API 请求 → FastAPI ==========
|
||||
handle /api/* {
|
||||
reverse_proxy localhost:8000 {
|
||||
# ✅ 从 Cloudflare 获取真实 IP
|
||||
header_up X-Real-IP {header.CF-Connecting-IP}
|
||||
header_up X-Forwarded-For {header.CF-Connecting-IP}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# ========== Shell 在线登录页 ==========
|
||||
handle /shell/* {
|
||||
root * /var/www/DesignerCEP/Server/static/shell
|
||||
try_files {path} {path}/ /shell/index.html
|
||||
file_server
|
||||
|
||||
@html path *.html
|
||||
header @html Cache-Control "no-cache, no-store, must-revalidate"
|
||||
|
||||
@assets path *.js *.css
|
||||
header @assets Cache-Control "public, max-age=31536000, immutable"
|
||||
}
|
||||
|
||||
# ========== Core 核心应用 ==========
|
||||
handle /core/* {
|
||||
root * /var/www/DesignerCEP/Server/static/core
|
||||
file_server
|
||||
|
||||
@html path *.html
|
||||
header @html Cache-Control "no-cache, no-store, must-revalidate"
|
||||
|
||||
@assets path *.js *.css
|
||||
header @assets Cache-Control "public, max-age=31536000, immutable"
|
||||
}
|
||||
|
||||
# ========== 下载文件 ==========
|
||||
handle /downloads/* {
|
||||
root * /var/www/DesignerCEP/Server/static/downloads
|
||||
file_server
|
||||
header Accept-Ranges bytes
|
||||
header Cache-Control "public, max-age=86400"
|
||||
}
|
||||
|
||||
# ========== 根路径重定向 ==========
|
||||
handle / {
|
||||
redir /shell/ permanent
|
||||
}
|
||||
|
||||
# ========== 通用配置 ==========
|
||||
encode gzip zstd
|
||||
|
||||
header {
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
-Server
|
||||
}
|
||||
|
||||
log {
|
||||
output file /var/log/caddy/designer-cep.log {
|
||||
roll_size 100mb
|
||||
roll_keep 10
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 4. 修改后端代码(支持 CEP 环境)
|
||||
|
||||
### 4.1 修改 FastAPI 的 CORS 配置
|
||||
|
||||
```python
|
||||
# Server/app/main.py
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import os
|
||||
|
||||
app = FastAPI(title=settings.PROJECT_NAME)
|
||||
|
||||
# 环境判断
|
||||
IS_DEV = os.getenv("ENV", "development") == "development"
|
||||
|
||||
# ========== CORS 配置 ==========
|
||||
if IS_DEV:
|
||||
# 开发环境:宽松配置
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
else:
|
||||
# 生产环境:严格配置
|
||||
allowed_origins = os.getenv("ALLOWED_ORIGINS", "").split(",")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=allowed_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# ✅ 特殊处理 CEP 环境(Origin: null)
|
||||
@app.middleware("http")
|
||||
async def cep_cors_middleware(request: Request, call_next):
|
||||
origin = request.headers.get("origin")
|
||||
|
||||
# CEP 环境的 Origin 是 null 或 cep://
|
||||
if origin in ["null", None] or (origin and origin.startswith("cep://")):
|
||||
response = await call_next(request)
|
||||
response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
response.headers["Access-Control-Allow-Credentials"] = "true"
|
||||
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
|
||||
response.headers["Access-Control-Allow-Headers"] = "*"
|
||||
return response
|
||||
|
||||
return await call_next(request)
|
||||
|
||||
# ========== API 路由(保持不变)==========
|
||||
app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["authentication"])
|
||||
app.include_router(client.router, prefix=f"{settings.API_V1_STR}/client", tags=["client"])
|
||||
app.include_router(admin.router, prefix=f"{settings.API_V1_STR}/admin", tags=["admin"])
|
||||
app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
|
||||
app.include_router(jsx_demo.router, prefix=f"{settings.API_V1_STR}/jsx_demo", tags=["jsx_demo"])
|
||||
|
||||
# ❌ 删除静态文件挂载(交给 Caddy 处理)
|
||||
# app.mount("/download", ...) # 删除
|
||||
# app.mount("/shell", ...) # 删除
|
||||
# app.mount("/core", ...) # 删除
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"message": "DesignerCEP API Server", "version": "1.0.0"}
|
||||
|
||||
@app.get("/health")
|
||||
def health_check():
|
||||
return {"status": "healthy"}
|
||||
|
||||
@app.on_event("startup")
|
||||
def on_startup():
|
||||
init_db()
|
||||
```
|
||||
|
||||
### 4.2 配置环境变量
|
||||
|
||||
```bash
|
||||
# Server/.env
|
||||
ENV=production
|
||||
PROJECT_NAME=DesignerCEP
|
||||
API_V1_STR=/api/v1
|
||||
SECRET_KEY=your-secret-key-here
|
||||
DATABASE_URL=mysql://user:password@localhost:3306/designer_cep
|
||||
|
||||
# 生产环境允许的来源
|
||||
ALLOWED_ORIGINS=https://your-domain.com,https://www.your-domain.com
|
||||
|
||||
# 是否由 FastAPI 提供静态文件(生产环境设为 false)
|
||||
SERVE_STATIC=false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 5. 部署 FastAPI 服务
|
||||
|
||||
### 5.1 使用 Systemd 管理服务
|
||||
|
||||
```bash
|
||||
# 创建服务文件
|
||||
sudo nano /etc/systemd/system/designer-cep.service
|
||||
```
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=DesignerCEP FastAPI Application
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
Group=www-data
|
||||
WorkingDirectory=/var/www/DesignerCEP/Server
|
||||
Environment="PATH=/var/www/DesignerCEP/Server/venv/bin"
|
||||
ExecStart=/var/www/DesignerCEP/Server/venv/bin/gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker -b 127.0.0.1:8000
|
||||
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 5.2 安装依赖并启动
|
||||
|
||||
```bash
|
||||
# 1. 创建虚拟环境
|
||||
cd /var/www/DesignerCEP/Server
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# 2. 安装依赖
|
||||
pip install -r requirements.txt
|
||||
pip install gunicorn uvicorn[standard]
|
||||
|
||||
# 3. 测试运行
|
||||
uvicorn app.main:app --host 127.0.0.1 --port 8000
|
||||
|
||||
# 4. 启动服务
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable designer-cep
|
||||
sudo systemctl start designer-cep
|
||||
|
||||
# 5. 检查状态
|
||||
sudo systemctl status designer-cep
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📤 6. 部署前端文件
|
||||
|
||||
### 6.1 配置前端环境
|
||||
|
||||
```bash
|
||||
# Designer/.env.production
|
||||
VITE_API_SERVER=https://your-domain.com
|
||||
```
|
||||
|
||||
### 6.2 构建前端
|
||||
|
||||
```bash
|
||||
cd Designer
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 6.3 上传到服务器
|
||||
|
||||
#### 方法 A: 手动上传
|
||||
|
||||
```bash
|
||||
# 在本地执行
|
||||
cd Designer/dist
|
||||
|
||||
# 上传 Shell
|
||||
scp -r Shell/* user@your-server:/var/www/DesignerCEP/Server/static/shell/
|
||||
|
||||
# 上传 Core
|
||||
scp -r Designer/* user@your-server:/var/www/DesignerCEP/Server/static/core/1.0.6/
|
||||
|
||||
# 打包并上传 Shell.zip
|
||||
zip -r shell-1.0.6.zip Shell/
|
||||
scp shell-1.0.6.zip user@your-server:/var/www/DesignerCEP/Server/static/downloads/
|
||||
```
|
||||
|
||||
#### 方法 B: 使用自动化脚本
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
cd AdminTool
|
||||
|
||||
# 首次配置
|
||||
python auto_deploy_core.py --version 1.0.6 --setup
|
||||
|
||||
# 输入配置信息:
|
||||
# - 服务器地址: your-domain.com
|
||||
# - SSH 端口: 22
|
||||
# - 用户名: root
|
||||
# - 密码: ******
|
||||
# - 远程路径: /var/www/DesignerCEP/Server/static
|
||||
|
||||
# 部署
|
||||
python auto_deploy_core.py --version 1.0.6 --deploy --update-db
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 7. 启动 Caddy
|
||||
|
||||
```bash
|
||||
# 检查配置语法
|
||||
sudo caddy validate --config /etc/caddy/Caddyfile
|
||||
|
||||
# 重启 Caddy
|
||||
sudo systemctl restart caddy
|
||||
|
||||
# 查看状态
|
||||
sudo systemctl status caddy
|
||||
|
||||
# 查看日志
|
||||
sudo journalctl -u caddy -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 8. 测试部署
|
||||
|
||||
### 8.1 测试静态文件
|
||||
|
||||
```bash
|
||||
# 测试 Shell
|
||||
curl -I https://your-domain.com/shell/
|
||||
|
||||
# 期望输出:
|
||||
# HTTP/2 200
|
||||
# content-type: text/html
|
||||
|
||||
# 测试 Core
|
||||
curl -I https://your-domain.com/core/1.0.6/
|
||||
|
||||
# 测试下载
|
||||
curl -I https://your-domain.com/downloads/shell-1.0.6.zip
|
||||
```
|
||||
|
||||
### 8.2 测试 API
|
||||
|
||||
```bash
|
||||
# 测试健康检查
|
||||
curl https://your-domain.com/api/v1/health
|
||||
|
||||
# 期望输出:
|
||||
# {"status":"healthy"}
|
||||
|
||||
# 测试登录接口
|
||||
curl -X POST https://your-domain.com/api/v1/client/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"test","password":"test","device_id":"test-device"}'
|
||||
```
|
||||
|
||||
### 8.3 测试 CEP CORS
|
||||
|
||||
```bash
|
||||
# 模拟 CEP 环境的预检请求
|
||||
curl -X OPTIONS https://your-domain.com/api/v1/client/login \
|
||||
-H "Origin: null" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-v
|
||||
|
||||
# 期望输出:
|
||||
# Access-Control-Allow-Origin: *
|
||||
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
|
||||
```
|
||||
|
||||
### 8.4 浏览器测试
|
||||
|
||||
1. 打开 `https://your-domain.com/shell/`
|
||||
2. 输入用户名密码登录
|
||||
3. 按 F12 打开开发者工具
|
||||
4. 查看 Network 标签:
|
||||
- ✅ 所有请求都是 HTTPS
|
||||
- ✅ API 请求有 `Authorization: Bearer xxx`
|
||||
- ✅ 没有 CORS 错误
|
||||
|
||||
---
|
||||
|
||||
## 🔍 9. 常见问题
|
||||
|
||||
### Q1: Caddy 自动证书失败
|
||||
|
||||
**原因**:域名 DNS 没有解析到服务器
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 检查 DNS 解析
|
||||
nslookup your-domain.com
|
||||
|
||||
# 确保 A 记录指向服务器 IP
|
||||
dig your-domain.com
|
||||
```
|
||||
|
||||
### Q2: API 请求 502 Bad Gateway
|
||||
|
||||
**原因**:FastAPI 服务没有启动
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 检查 FastAPI 状态
|
||||
sudo systemctl status designer-cep
|
||||
|
||||
# 查看日志
|
||||
sudo journalctl -u designer-cep -f
|
||||
|
||||
# 重启服务
|
||||
sudo systemctl restart designer-cep
|
||||
```
|
||||
|
||||
### Q3: 静态文件 404
|
||||
|
||||
**原因**:文件路径不对
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 检查文件是否存在
|
||||
ls -la /var/www/DesignerCEP/Server/static/shell/
|
||||
ls -la /var/www/DesignerCEP/Server/static/core/1.0.6/
|
||||
|
||||
# 检查权限
|
||||
sudo chown -R www-data:www-data /var/www/DesignerCEP
|
||||
sudo chmod -R 755 /var/www/DesignerCEP
|
||||
```
|
||||
|
||||
### Q4: Cloudflare 无限重定向
|
||||
|
||||
**原因**:Cloudflare SSL 模式设置错误
|
||||
|
||||
**解决**:
|
||||
1. 进入 Cloudflare 控制台
|
||||
2. SSL/TLS → Overview
|
||||
3. 选择 **"Flexible"** 模式(因为 Caddy 配置的是 HTTP)
|
||||
|
||||
或者修改 Caddyfile 使用 HTTPS:
|
||||
|
||||
```caddy
|
||||
# 方法 1: 自签名证书
|
||||
your-domain.com {
|
||||
tls internal
|
||||
# ... 其他配置
|
||||
}
|
||||
|
||||
# 方法 2: Cloudflare Origin Certificate
|
||||
your-domain.com {
|
||||
tls /etc/caddy/certs/cloudflare-origin.pem /etc/caddy/certs/cloudflare-origin.key
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 10. Cloudflare 最佳实践
|
||||
|
||||
### 10.1 SSL/TLS 配置
|
||||
|
||||
```
|
||||
Cloudflare 控制台 → SSL/TLS → Overview
|
||||
|
||||
选择模式:
|
||||
- ❌ Off - 不使用 HTTPS
|
||||
- ✅ Flexible - Cloudflare ←HTTPS→ 用户,Cloudflare ←HTTP→ 源站(推荐简单部署)
|
||||
- ⚠️ Full - 双向 HTTPS,但不验证源站证书
|
||||
- ✅ Full (Strict) - 双向 HTTPS,验证证书(推荐生产环境)
|
||||
```
|
||||
|
||||
**推荐方案**:
|
||||
|
||||
#### Flexible 模式(最简单)
|
||||
|
||||
```caddy
|
||||
# Caddyfile
|
||||
{
|
||||
auto_https off
|
||||
}
|
||||
|
||||
http://your-domain.com {
|
||||
# ... 配置
|
||||
}
|
||||
```
|
||||
|
||||
Cloudflare 设置:`Flexible`
|
||||
|
||||
#### Full (Strict) 模式(最安全)
|
||||
|
||||
1. 生成 Cloudflare Origin Certificate:
|
||||
```
|
||||
Cloudflare 控制台 → SSL/TLS → Origin Server → Create Certificate
|
||||
```
|
||||
|
||||
2. 下载证书并上传到服务器:
|
||||
```bash
|
||||
sudo mkdir -p /etc/caddy/certs
|
||||
sudo nano /etc/caddy/certs/cloudflare-origin.pem # 粘贴证书
|
||||
sudo nano /etc/caddy/certs/cloudflare-origin.key # 粘贴私钥
|
||||
sudo chmod 600 /etc/caddy/certs/*
|
||||
```
|
||||
|
||||
3. 修改 Caddyfile:
|
||||
```caddy
|
||||
your-domain.com {
|
||||
tls /etc/caddy/certs/cloudflare-origin.pem /etc/caddy/certs/cloudflare-origin.key
|
||||
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
4. Cloudflare 设置:`Full (Strict)`
|
||||
|
||||
### 10.2 性能优化
|
||||
|
||||
```
|
||||
Cloudflare 控制台设置:
|
||||
|
||||
1. Speed → Optimization
|
||||
- ✅ Auto Minify: HTML, CSS, JS
|
||||
- ✅ Brotli 压缩
|
||||
|
||||
2. Caching → Configuration
|
||||
- ✅ Caching Level: Standard
|
||||
- ✅ Browser Cache TTL: Respect Existing Headers
|
||||
|
||||
3. Network
|
||||
- ✅ HTTP/2
|
||||
- ✅ HTTP/3 (QUIC)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 11. 监控和日志
|
||||
|
||||
### 查看 Caddy 日志
|
||||
|
||||
```bash
|
||||
# 实时日志
|
||||
sudo journalctl -u caddy -f
|
||||
|
||||
# 访问日志
|
||||
sudo tail -f /var/log/caddy/designer-cep.log
|
||||
|
||||
# 错误日志
|
||||
sudo journalctl -u caddy --since "1 hour ago" | grep -i error
|
||||
```
|
||||
|
||||
### 查看 FastAPI 日志
|
||||
|
||||
```bash
|
||||
# 实时日志
|
||||
sudo journalctl -u designer-cep -f
|
||||
|
||||
# 最近的错误
|
||||
sudo journalctl -u designer-cep --since "1 hour ago" | grep -i error
|
||||
```
|
||||
|
||||
### 性能监控
|
||||
|
||||
```bash
|
||||
# 检查服务器资源
|
||||
htop
|
||||
|
||||
# 检查端口监听
|
||||
sudo netstat -tlnp | grep -E '(8000|80|443)'
|
||||
|
||||
# 检查 Caddy 进程
|
||||
ps aux | grep caddy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 12. 更新部署
|
||||
|
||||
### 更新前端
|
||||
|
||||
```bash
|
||||
# 方法 A: 手动
|
||||
cd Designer
|
||||
npm run build
|
||||
scp -r dist/Shell/* user@server:/var/www/DesignerCEP/Server/static/shell/
|
||||
scp -r dist/Designer/* user@server:/var/www/DesignerCEP/Server/static/core/1.0.7/
|
||||
|
||||
# 方法 B: 自动化
|
||||
cd AdminTool
|
||||
python auto_deploy_core.py --version 1.0.7 --deploy --update-db
|
||||
```
|
||||
|
||||
### 更新后端
|
||||
|
||||
```bash
|
||||
# 1. SSH 到服务器
|
||||
ssh user@your-server
|
||||
|
||||
# 2. 拉取最新代码
|
||||
cd /var/www/DesignerCEP/Server
|
||||
git pull
|
||||
|
||||
# 3. 更新依赖
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 4. 重启服务
|
||||
sudo systemctl restart designer-cep
|
||||
|
||||
# 5. 检查状态
|
||||
sudo systemctl status designer-cep
|
||||
```
|
||||
|
||||
### 更新 Caddy 配置
|
||||
|
||||
```bash
|
||||
# 1. 修改配置
|
||||
sudo nano /etc/caddy/Caddyfile
|
||||
|
||||
# 2. 验证配置
|
||||
sudo caddy validate --config /etc/caddy/Caddyfile
|
||||
|
||||
# 3. 重新加载(不中断服务)
|
||||
sudo systemctl reload caddy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 完成!
|
||||
|
||||
访问地址:
|
||||
- **Shell 登录页**:`https://your-domain.com/shell/`
|
||||
- **Core 应用**:`https://your-domain.com/core/1.0.6/`
|
||||
- **API 文档**:`https://your-domain.com/api/v1/docs`
|
||||
|
||||
---
|
||||
|
||||
## 📋 部署检查清单
|
||||
|
||||
- [ ] Caddy 已安装并启动
|
||||
- [ ] Caddyfile 配置正确
|
||||
- [ ] FastAPI 服务运行正常
|
||||
- [ ] 前端文件已上传
|
||||
- [ ] 环境变量配置正确
|
||||
- [ ] CORS 支持 CEP 环境
|
||||
- [ ] MySQL 数据库已更新
|
||||
- [ ] HTTPS 证书正常
|
||||
- [ ] 静态文件缓存正确
|
||||
- [ ] API 请求正常
|
||||
- [ ] CEP 扩展测试通过
|
||||
- [ ] 浏览器访问正常
|
||||
|
||||
---
|
||||
|
||||
**Caddy 的优势总结**:
|
||||
- ✅ 配置只有 Nginx 的 1/3 长度
|
||||
- ✅ 自动 HTTPS,无需 Certbot
|
||||
- ✅ 自动续期证书
|
||||
- ✅ 内置 GZIP/Brotli 压缩
|
||||
- ✅ 配置更直观易懂
|
||||
- ✅ 错误提示更友好
|
||||
|
||||
**下一步**:运行 `python auto_deploy_core.py --version 1.0.6 --deploy --update-db` 一键部署!
|
||||
|
||||
|
||||
193
temp_backup/Server_redundant/tempdocs/JSX加载失败问题分析与解决方案.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# JSX 加载失败问题分析与解决方案
|
||||
|
||||
## 📋 问题描述
|
||||
|
||||
在 Shell + Core 架构分离后,Core 应用中的 JSX 脚本无法正常运行,报错:
|
||||
|
||||
```
|
||||
Error: EvalScript error.
|
||||
```
|
||||
|
||||
尽管 JSX 文件路径找到了(`C:/Users/35780/AppData/Roaming/DesignerCache/v1.0.5/jsx/index.js`),但 `evalScript` 执行失败。
|
||||
|
||||
## 🔍 根本原因
|
||||
|
||||
### 问题1:JSX 构建格式错误
|
||||
|
||||
**位置**:`Designer/plugins/buildJsx/index.ts` 第 118 行
|
||||
|
||||
```typescript
|
||||
await bundle.write({
|
||||
file: output,
|
||||
format: 'cjs' // ❌ 问题所在!
|
||||
});
|
||||
```
|
||||
|
||||
**影响**:生成的 JSX 文件使用了 **CommonJS 格式**,包含:
|
||||
|
||||
```javascript
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var index_1 = require("./function/index"); // ❌ ExtendScript 不支持 require()
|
||||
var ActionManager = require("./utils/ActionManager");
|
||||
```
|
||||
|
||||
### 问题2:ExtendScript 环境限制
|
||||
|
||||
**ExtendScript (Adobe JSX 引擎) 不支持:**
|
||||
- ❌ CommonJS 的 `require()` 和 `exports`
|
||||
- ❌ ES6 的 `import/export`
|
||||
- ❌ `'use strict'` 严格模式
|
||||
|
||||
**ExtendScript 只支持:**
|
||||
- ✅ 全局函数
|
||||
- ✅ 立即执行函数表达式 (IIFE)
|
||||
- ✅ ES3/ES5 语法
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 方案 1:修改构建配置(长期方案)
|
||||
|
||||
修改 `Designer/plugins/buildJsx/index.ts`:
|
||||
|
||||
```typescript
|
||||
await bundle.write({
|
||||
file: output,
|
||||
format: 'iife', // ✅ 改为 IIFE 格式
|
||||
name: 'JSXBundle',
|
||||
strict: false // ✅ 禁用严格模式
|
||||
});
|
||||
```
|
||||
|
||||
**同时修改 TypeScript 配置**:
|
||||
|
||||
```typescript
|
||||
typescript({
|
||||
compilerOptions: {
|
||||
target: 'es5',
|
||||
strict: false,
|
||||
moduleResolution: 'node',
|
||||
esModuleInterop: true,
|
||||
skipLibCheck: true,
|
||||
// ...
|
||||
},
|
||||
tsconfig: false,
|
||||
}),
|
||||
```
|
||||
|
||||
### 方案 2:临时修复(已应用)
|
||||
|
||||
**已完成的操作:**
|
||||
|
||||
1. ✅ 创建了简化版本的 JSX 文件 (`index_fixed.js`)
|
||||
2. ✅ 移除了所有 CommonJS 语法
|
||||
3. ✅ 将依赖项(Logger, ActionManager)内联到单文件中
|
||||
4. ✅ 备份原文件为 `index.js.backup`
|
||||
5. ✅ 替换为修复后的版本
|
||||
|
||||
**文件位置**:
|
||||
- 修复后:`Designer/dist_core/jsx/index.js`
|
||||
- 备份:`Designer/dist_core/jsx/index.js.backup`
|
||||
|
||||
## 🧪 测试步骤
|
||||
|
||||
### 1. 清除客户端缓存
|
||||
|
||||
```powershell
|
||||
Remove-Item -Recurse -Force "$env:APPDATA\DesignerCache" -ErrorAction SilentlyContinue
|
||||
```
|
||||
|
||||
### 2. 重启插件
|
||||
|
||||
在 Photoshop 中:
|
||||
1. 关闭当前的 DesignerCEP 插件
|
||||
2. 重新打开插件
|
||||
3. 登录账号
|
||||
|
||||
### 3. 检查控制台
|
||||
|
||||
打开浏览器开发者工具(F12),查看:
|
||||
|
||||
**预期成功日志:**
|
||||
```
|
||||
[__LDX] Detected Core version v1.0.5
|
||||
[__LDX] Success: C:/Users/35780/AppData/Roaming/DesignerCache/v1.0.5/jsx/index.js
|
||||
宿主APP名称:Adobe Photoshop 2024
|
||||
```
|
||||
|
||||
**如果仍然失败,查看:**
|
||||
- 是否有 `EvalScript error`
|
||||
- ExtendScript 控制台的详细错误信息
|
||||
|
||||
### 4. 测试功能
|
||||
|
||||
在插件主界面点击"测试 JSX 调用"按钮,验证:
|
||||
- ✅ `getAppName()` 返回 "Adobe Photoshop"
|
||||
- ✅ `createLayer()` 可以创建新图层
|
||||
- ✅ `testMergeLayers()` 可以合并图层
|
||||
|
||||
## 📝 后续工作
|
||||
|
||||
### 短期(紧急)
|
||||
- [ ] 测试修复后的 JSX 是否正常工作
|
||||
- [ ] 如果测试通过,将修复后的文件上传到服务器
|
||||
|
||||
### 中期(优化)
|
||||
- [ ] 修复构建流程,确保未来构建自动生成正确格式
|
||||
- [ ] 解决 `g_logger` 和 `ActionManager` 的依赖打包问题
|
||||
- [ ] 更新 `deploy_core.py` 脚本,包含 JSX 构建步骤
|
||||
|
||||
### 长期(架构改进)
|
||||
- [ ] 评估是否需要保留完整的 Logger 和 ActionManager
|
||||
- [ ] 考虑使用 Rollup 的 `inlineDynamicImports` 选项
|
||||
- [ ] 编写自动化测试验证 JSX 在 ExtendScript 环境中的兼容性
|
||||
|
||||
## 🔗 相关文件
|
||||
|
||||
| 文件路径 | 说明 |
|
||||
|:---------|:-----|
|
||||
| `Designer/plugins/buildJsx/index.ts` | JSX 构建配置(已修改format为iife) |
|
||||
| `Designer/dist_core/jsx/index.js` | 修复后的 JSX 文件 |
|
||||
| `Designer/dist_core/jsx/index.js.backup` | 原始的 CommonJS 格式备份 |
|
||||
| `Designer/build_jsx_simple.mjs` | 简化的构建脚本(待完善) |
|
||||
|
||||
## 💡 技术要点
|
||||
|
||||
### ExtendScript 兼容性清单
|
||||
|
||||
```javascript
|
||||
✅ 允许:
|
||||
- var, function, 普通对象
|
||||
- IIFE: (function(){ ... })();
|
||||
- 全局赋值: $.global.xxx = function(){}
|
||||
- ES5 方法: Array.forEach, Object.keys
|
||||
|
||||
❌ 禁止:
|
||||
- 'use strict'
|
||||
- require() / exports / module.exports
|
||||
- import / export
|
||||
- const / let (部分版本不支持)
|
||||
- Promise / async/await (需 polyfill)
|
||||
- 箭头函数 (部分版本)
|
||||
```
|
||||
|
||||
### Rollup 输出格式对比
|
||||
|
||||
| 格式 | ExtendScript | 说明 |
|
||||
|:-----|:------------|:-----|
|
||||
| `cjs` | ❌ | CommonJS,使用 require/exports |
|
||||
| `esm` | ❌ | ES Module,使用 import/export |
|
||||
| `iife` | ✅ | 立即执行函数,所有代码在一个作用域 |
|
||||
| `umd` | ⚠️ | 通用模块,体积较大但兼容性好 |
|
||||
|
||||
## 📞 联系与支持
|
||||
|
||||
如果测试中遇到问题,请提供:
|
||||
1. 浏览器控制台的完整错误日志
|
||||
2. ExtendScript Toolkit 的错误信息(如果有)
|
||||
3. Photoshop 版本和操作系统信息
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**:2025-12-16
|
||||
**问题状态**:✅ 已修复(待测试验证)
|
||||
|
||||
128
temp_backup/Server_redundant/tempdocs/JSX方法测试指南.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# JSX 方法测试指南
|
||||
|
||||
## 🎯 目标
|
||||
|
||||
测试 5 种不同的 JSX 实现方法,找出在 ExtendScript 环境中能正常运行的方式。
|
||||
|
||||
## 📋 测试方法列表
|
||||
|
||||
| 方法 | 实现方式 | 特点 |
|
||||
|:-----|:---------|:-----|
|
||||
| **方法1** | DOM API | 最简单,直接使用 PS 的 DOM 对象 |
|
||||
| **方法2** | ActionManager | 使用 ActionDescriptor,更底层 |
|
||||
| **方法3** | Object Helper | 使用对象字面量封装 |
|
||||
| **方法4** | ES3 Class | 使用原型链的类实现 |
|
||||
| **方法5** | PSApi Style | 模仿完整的 PSApi 结构 |
|
||||
|
||||
## 🧪 快速测试步骤
|
||||
|
||||
### 1. 替换 JSX 文件
|
||||
|
||||
```powershell
|
||||
# 方式A:直接复制到缓存(推荐)
|
||||
Copy-Item Designer\test_jsx_methods.js "$env:APPDATA\DesignerCache\v1.0.7\jsx\index.js" -Force
|
||||
|
||||
# 方式B:或者重新打包上传
|
||||
# (如果方式A不行,说明缓存路径不对)
|
||||
```
|
||||
|
||||
### 2. 在浏览器控制台测试
|
||||
|
||||
打开 Photoshop 插件,按 F12 打开控制台,逐个测试:
|
||||
|
||||
```javascript
|
||||
// 测试方法1(DOM API)
|
||||
cep.evalScript("createLayerMethod1('测试图层1')").then(r => console.log('方法1:', r))
|
||||
|
||||
// 测试方法2(ActionManager)
|
||||
cep.evalScript("createLayerMethod2('测试图层2')").then(r => console.log('方法2:', r))
|
||||
|
||||
// 测试方法3(Object Helper)
|
||||
cep.evalScript("createLayerMethod3('测试图层3')").then(r => console.log('方法3:', r))
|
||||
|
||||
// 测试方法4(ES3 Class)
|
||||
cep.evalScript("createLayerMethod4('测试图层4')").then(r => console.log('方法4:', r))
|
||||
|
||||
// 测试方法5(PSApi Style)
|
||||
cep.evalScript("createLayerMethod5('测试图层5')").then(r => console.log('方法5:', r))
|
||||
|
||||
// 测试获取图层数量
|
||||
cep.evalScript("getLayerCount()").then(r => console.log('图层数量:', r))
|
||||
```
|
||||
|
||||
### 3. 观察结果
|
||||
|
||||
**成功的结果示例:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"method": "DOM API",
|
||||
"layerName": "测试图层1"
|
||||
}
|
||||
```
|
||||
|
||||
**失败的结果示例:**
|
||||
```json
|
||||
{
|
||||
"error": "..."
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 预期哪个会成功?
|
||||
|
||||
根据 ExtendScript 的特性,**最可能成功的是**:
|
||||
|
||||
1. ✅ **方法1(DOM API)** - 最简单直接
|
||||
2. ✅ **方法3(Object Helper)** - 使用对象字面量
|
||||
3. ⚠️ **方法2(ActionManager)** - 可能因为 API 调用问题失败
|
||||
4. ⚠️ **方法4(ES3 Class)** - 依赖原型链
|
||||
5. ⚠️ **方法5(PSApi Style)** - 最复杂,可能有嵌套问题
|
||||
|
||||
## 🔧 如果都失败了
|
||||
|
||||
如果所有方法都失败,可能的原因:
|
||||
|
||||
1. **JSX 文件没有被加载**
|
||||
- 检查控制台是否有 `Test JSX Methods Loaded` 日志
|
||||
|
||||
2. **路径不对**
|
||||
- 检查 `[__LDX]` 日志显示的路径
|
||||
- 手动找到该路径,确认文件内容
|
||||
|
||||
3. **ExtendScript 语法错误**
|
||||
- 在 ExtendScript Toolkit 中打开文件检查
|
||||
|
||||
## 💡 成功后的下一步
|
||||
|
||||
找到能工作的方法后:
|
||||
|
||||
1. 📝 **记录成功的方法**(如"方法1成功")
|
||||
2. 🔄 **使用该方法重写所有 JSX 函数**
|
||||
3. 📦 **重新构建和打包 Core**
|
||||
4. 🚀 **发布到服务器**
|
||||
|
||||
## 🎨 添加前端测试按钮(可选)
|
||||
|
||||
在 `Home.vue` 中添加测试按钮:
|
||||
|
||||
```vue
|
||||
<a-button @click="testMethod1">测试方法1</a-button>
|
||||
<a-button @click="testMethod2">测试方法2</a-button>
|
||||
<a-button @click="testMethod3">测试方法3</a-button>
|
||||
<a-button @click="testMethod4">测试方法4</a-button>
|
||||
<a-button @click="testMethod5">测试方法5</a-button>
|
||||
```
|
||||
|
||||
```typescript
|
||||
const testMethod1 = async () => {
|
||||
const res = await jsxApi.evalScript("createLayerMethod1('测试1')");
|
||||
console.log('方法1结果:', res);
|
||||
Message.info(res);
|
||||
};
|
||||
// ... 类似地添加其他方法
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**现在开始测试吧!** 告诉我哪个方法能成功 🎯
|
||||
|
||||
212
temp_backup/Server_redundant/tempdocs/PSApi功能测试指南.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# PSApi 功能测试指南
|
||||
|
||||
## 📋 已完成的修改
|
||||
|
||||
### 1. JSX 端(`src/jsx/index.ts`)
|
||||
|
||||
**添加的内容:**
|
||||
```typescript
|
||||
import { PSApi } from "./function/ps/api" // 导入 PSApi 类
|
||||
|
||||
const psApi = new PSApi() // 实例化 PSApi
|
||||
|
||||
// 新增测试函数
|
||||
export function testPSApi() {
|
||||
try {
|
||||
const selectLength = psApi.layer.getSelectLength(); // 调用 PSApi 的方法
|
||||
|
||||
return JSON_EX.stringify({
|
||||
success: true,
|
||||
message: "PSApi 调用成功!",
|
||||
selectLength: selectLength,
|
||||
info: "当前选中的图层数量: " + selectLength
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON_EX.stringify({ error: error.toString() });
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露到全局
|
||||
($.global as any).testPSApi = testPSApi;
|
||||
```
|
||||
|
||||
### 2. 前端 API(`src/api/jsxApi/evalJSX.ts`)
|
||||
|
||||
**添加的内容:**
|
||||
```typescript
|
||||
testPSApi: async () => {
|
||||
const jsonStr = await cep.evalScript("testPSApi()");
|
||||
const res = JSON.parse(jsonStr);
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 前端界面(`src/view/Home.vue`)
|
||||
|
||||
**添加的内容:**
|
||||
- ✅ 新增"测试 PSApi"按钮
|
||||
- ✅ 添加 `handleTestPSApi()` 处理函数
|
||||
|
||||
## 🧪 测试步骤
|
||||
|
||||
### 步骤 1:重新构建(如果使用简化版)
|
||||
|
||||
如果您还在使用临时的简化版 JSX,现在可以:
|
||||
|
||||
**选项 A:使用简化版测试(推荐先测)**
|
||||
```powershell
|
||||
# 当前的 dist_core/jsx/index.js 已经包含简化版
|
||||
# 直接测试即可
|
||||
```
|
||||
|
||||
**选项 B:重新构建完整版(包含 PSApi)**
|
||||
```powershell
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
|
||||
# 重新构建 Core(会包含新的 testPSApi 函数)
|
||||
npm run build:core
|
||||
|
||||
# 如果构建失败,需要先修复构建流程
|
||||
# 或者手动更新 dist_core/jsx/index.js
|
||||
```
|
||||
|
||||
### 步骤 2:清除缓存
|
||||
|
||||
```powershell
|
||||
Remove-Item -Recurse -Force "$env:APPDATA\DesignerCache" -ErrorAction SilentlyContinue
|
||||
```
|
||||
|
||||
### 步骤 3:在 Photoshop 中测试
|
||||
|
||||
1. **打开 Photoshop**
|
||||
2. **打开 DesignerCEP 插件**
|
||||
3. **登录账号**(会下载最新的 Core)
|
||||
4. **在主界面点击"测试 PSApi"按钮**
|
||||
|
||||
### 步骤 4:观察结果
|
||||
|
||||
**预期成功的结果:**
|
||||
|
||||
**控制台日志:**
|
||||
```
|
||||
[__LDX] Detected Core version v1.0.5
|
||||
[__LDX] Success: C:/Users/.../jsx/index.js
|
||||
Call testPSApi - Testing PSApi from api.ts
|
||||
```
|
||||
|
||||
**前端界面:**
|
||||
- 弹出成功提示:`PSApi 调用成功! - 当前选中的图层数量: 1`
|
||||
- 显示选中图层数(如果没选图层则为 0)
|
||||
|
||||
**如果失败:**
|
||||
- 查看浏览器控制台的错误信息
|
||||
- 查看 ExtendScript Toolkit 的错误(如果有)
|
||||
|
||||
## 📊 测试场景
|
||||
|
||||
### 场景 1:没有选中图层
|
||||
- **操作**:不选择任何图层,点击"测试 PSApi"
|
||||
- **预期**:显示"选中图层数: 0"
|
||||
|
||||
### 场景 2:选中 1 个图层
|
||||
- **操作**:在 PS 中选中 1 个图层,点击"测试 PSApi"
|
||||
- **预期**:显示"选中图层数: 1"
|
||||
|
||||
### 场景 3:选中多个图层
|
||||
- **操作**:按住 Ctrl/Cmd 选中多个图层,点击"测试 PSApi"
|
||||
- **预期**:显示"选中图层数: N"(N 为实际选中数量)
|
||||
|
||||
## 🎯 测试的意义
|
||||
|
||||
### 如果测试成功,证明:
|
||||
|
||||
1. ✅ **可以在 JSX 中使用完整的 `api.ts`**
|
||||
2. ✅ **PSApi 类的方法可以正常调用**
|
||||
3. ✅ **面向对象的写法在 ExtendScript 中运行正常**
|
||||
4. ✅ **可以放心使用 api.ts 中的所有功能**
|
||||
|
||||
### 如果测试成功,您可以:
|
||||
|
||||
```typescript
|
||||
// 在 src/jsx/index.ts 中使用任何 PSApi 的功能
|
||||
|
||||
// 示例 1:图层操作
|
||||
export function selectLayerById(id: number) {
|
||||
psApi.layer.onIdSelectLayer(id);
|
||||
return JSON_EX.stringify({ success: true });
|
||||
}
|
||||
|
||||
// 示例 2:文档操作
|
||||
export function getDocumentSize() {
|
||||
const size = psApi.doc.getSize();
|
||||
return JSON_EX.stringify({
|
||||
width: size.cx,
|
||||
height: size.cy
|
||||
});
|
||||
}
|
||||
|
||||
// 示例 3:颜色操作
|
||||
export function setFillColor(hex: string) {
|
||||
const col = new SolidColor();
|
||||
col.rgb.hexValue = hex;
|
||||
psApi.activeLayer.shape.setFillColor(true, col);
|
||||
return JSON_EX.stringify({ success: true });
|
||||
}
|
||||
|
||||
// 示例 4:字体操作
|
||||
export function changeFontFamily(postScriptName: string, name: string, style: string) {
|
||||
psApi.font.setFontName(postScriptName, name, style);
|
||||
return JSON_EX.stringify({ success: true });
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 常见问题
|
||||
|
||||
### Q1: 点击按钮没有反应
|
||||
**原因:** JSX 未加载或函数未暴露到全局
|
||||
**解决:**
|
||||
- 检查控制台是否有 `[__LDX] Success` 日志
|
||||
- 确认 `testPSApi` 已添加到 `$.global`
|
||||
|
||||
### Q2: 报错 "PSApi is not defined"
|
||||
**原因:** 构建时未包含 `api.ts`
|
||||
**解决:**
|
||||
- 确认 `src/jsx/index.ts` 中有 `import { PSApi } from "./function/ps/api"`
|
||||
- 重新构建或检查构建配置
|
||||
|
||||
### Q3: 报错 "getSelectLength is not a function"
|
||||
**原因:** PSApi 的方法没有正确编译
|
||||
**解决:**
|
||||
- 检查 `api.ts` 是否有语法错误
|
||||
- 确认构建格式为 IIFE(我们已修改)
|
||||
- 查看 `dist_core/jsx/index.js` 是否包含 PSApi 的代码
|
||||
|
||||
### Q4: 构建时报错 "g_logger is not exported"
|
||||
**原因:** 模块打包问题(我们之前遇到的)
|
||||
**解决:**
|
||||
- 暂时使用简化版测试
|
||||
- 或者手动修复构建流程
|
||||
- 或者等待我们修复完整版构建
|
||||
|
||||
## 💡 下一步计划
|
||||
|
||||
### 如果测试通过:
|
||||
1. ✅ 确认可以使用完整的 `api.ts`
|
||||
2. 🔄 修复完整版的构建流程
|
||||
3. 📦 重新打包包含所有功能的 Core
|
||||
4. 🚀 上传到服务器供用户使用
|
||||
|
||||
### 如果测试失败:
|
||||
1. 📝 记录详细的错误信息
|
||||
2. 🔍 分析是构建问题还是代码问题
|
||||
3. 🔧 逐步调试修复
|
||||
|
||||
---
|
||||
|
||||
**准备好了吗?开始测试吧!** 🚀
|
||||
|
||||
测试完成后,请告诉我:
|
||||
- ✅ 测试成功还是失败
|
||||
- 📋 控制台的完整日志
|
||||
- 💬 遇到的任何问题或错误信息
|
||||
|
||||
80
temp_backup/Server_redundant/tempdocs/README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 🚀 DesignerCEP 快速部署
|
||||
|
||||
## 📐 架构
|
||||
|
||||
```
|
||||
app.aidg168.uk → 前端(Caddy)
|
||||
backend.aidg168.uk → 后端 API(FastAPI + MySQL)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 快速部署(3 步)
|
||||
|
||||
### 步骤 1:本地构建
|
||||
|
||||
```powershell
|
||||
# 构建前端
|
||||
cd Designer
|
||||
npm run build:core
|
||||
|
||||
# 复制到 Server
|
||||
cd ..
|
||||
xcopy /E /Y Designer\dist_core\* Server\static\app\
|
||||
|
||||
# 打包 Server
|
||||
Compress-Archive -Path Server\* -DestinationPath Server.zip -Force
|
||||
```
|
||||
|
||||
### 步骤 2:上传到服务器
|
||||
|
||||
```powershell
|
||||
# 上传文件
|
||||
scp Server.zip root@103.97.201.136:/root/
|
||||
scp Caddyfile root@103.97.201.136:/etc/caddy/Caddyfile
|
||||
```
|
||||
|
||||
### 步骤 3:服务器部署
|
||||
|
||||
```bash
|
||||
# SSH 登录
|
||||
ssh root@103.97.201.136
|
||||
|
||||
# 解压
|
||||
cd /root
|
||||
unzip -o Server.zip -d server
|
||||
|
||||
# 启动服务
|
||||
cd server
|
||||
docker-compose up -d
|
||||
|
||||
# 查看日志
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验证
|
||||
|
||||
访问:`https://app.aidg168.uk/`
|
||||
|
||||
---
|
||||
|
||||
## 📁 服务器目录结构
|
||||
|
||||
```
|
||||
/root/server/
|
||||
├── app/ ← 后端代码
|
||||
├── static/ ← 前端文件
|
||||
│ └── app/ ← 前端构建产物
|
||||
│ ├── index.html
|
||||
│ └── assets/
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile
|
||||
└── .env
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
详细文档:查看 `Server/部署到服务器.md`
|
||||
|
||||
83
temp_backup/Server_redundant/tempdocs/Shell架构改造任务清单.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Shell 架构改造任务清单 (增强版)
|
||||
|
||||
基于您提供的 `serveradmin` 和 `upload` 逻辑,我们引入了 **“用户分组 (Gray Release)”** 和 **“一键发布 (CI/CD)”** 机制。
|
||||
|
||||
## 1. 插件端 (Frontend - Shell)
|
||||
|
||||
Shell 端不需要太大变化,主要是版本检查接口需要携带用户信息。
|
||||
|
||||
- `src/launcher/utils/`
|
||||
- [ ] **增强版版本检查**:
|
||||
- 请求 `POST /api/check_update`
|
||||
- 参数: `{ username: "当前登录用户" }`
|
||||
- 逻辑: 服务端会根据用户所在的组,返回不同的版本。普通用户返回 `v1.0`,测试组用户返回 `v1.1-beta`。
|
||||
|
||||
## 2. 服务端 (Backend - Python)
|
||||
|
||||
参考 `serveradmin` 逻辑进行改造。
|
||||
|
||||
### 2.1 数据库结构升级
|
||||
|
||||
需要支持不同用户获取不同版本。
|
||||
|
||||
- **表 `psmark_group` (用户组)**:
|
||||
|
||||
- `id`: int (主键)
|
||||
- `name`: string (组名,如 "Stable", "Beta")
|
||||
- `current_version`: string (该组当前使用的版本文件名,如 "release_v1.0.zip")
|
||||
- `comment`: string
|
||||
|
||||
- **表 `user` (原有表增强)**:
|
||||
- `group_id`: int (外键,关联 psmark_group)
|
||||
|
||||
### 2.2 接口逻辑增强
|
||||
|
||||
- `POST /api/check_update`:
|
||||
1. 根据 `username` 查 `group_id`。
|
||||
2. 查 `psmark_group` 表获取该组的 `current_version`。
|
||||
3. 返回该版本的下载地址。
|
||||
- **优势**: 您可以在后台把某个测试用户切到 "DevGroup",他重启插件就会立刻下载最新的测试版,而其他用户不受影响。
|
||||
|
||||
---
|
||||
|
||||
## 3. 发布流程 (Publishing Workflow)
|
||||
|
||||
为了实现“上传时编译”,我们将编写一个自动化脚本。
|
||||
|
||||
### 3.1 自动化发布脚本 (`scripts/publish.ts`)
|
||||
|
||||
这个脚本将在您的开发机上运行,一键完成所有操作。
|
||||
|
||||
- **执行流程**:
|
||||
|
||||
1. **Compile**: 运行 `npm run build:core` (生成 `dist/core`)。
|
||||
2. **Package**: 使用 `adm-zip` 将 `dist/core` 压缩为 `code_{timestamp}.zip`。
|
||||
3. **Upload**: 调用服务端接口 `POST /archives` 将 ZIP 上传。
|
||||
4. **Register (可选)**: 自动调用接口将新版本注册到数据库(可选)。
|
||||
|
||||
- **命令**:
|
||||
```bash
|
||||
npm run publish # 一键发布
|
||||
```
|
||||
|
||||
### 3.2 服务端支持
|
||||
|
||||
- 确保 `POST /archives` 接口能接收文件并保存到 `archives/` 目录 (参考 `server.py` 已有逻辑)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 后台管理工具 (Admin Tool)
|
||||
|
||||
您可以直接复用或升级现有的 PyQt 工具 (`upload/admin.py`)。
|
||||
|
||||
- **功能需求**:
|
||||
- [ ] **用户分理**: 设置用户的 `group_id`。
|
||||
- [ ] **版本指派**: 设置某个 Group 使用哪个 ZIP 版本。
|
||||
|
||||
---
|
||||
|
||||
## 5. 执行路线图
|
||||
|
||||
1. **Day 1 (Publishing)**: 写好 `scripts/publish.ts`,确保能一键编译并上传 ZIP 到您的服务器。
|
||||
2. **Day 2 (Backend)**: 改造 Python 后端,加入 Group 表,在此基础上实现“根据用户返回版本”的接口。
|
||||
3. **Day 3 (Shell)**: 开发插件 Shell 的下载器,对接上述接口。
|
||||
88
temp_backup/Server_redundant/tempdocs/Shell架构改造计划.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 壳架构 (Shell Architecture) 改造计划
|
||||
|
||||
为了实现“登录即更新”和“代码保护”,我们将把现有的 `DesignerCEP` 单体项目拆分为 **壳 (Shell)** 和 **核 (Core)** 两部分。
|
||||
|
||||
## 1. 架构概览
|
||||
|
||||
| 模块 | 名称 | 职责 | 部署位置 | 更新频率 |
|
||||
| :-------- | :---------------- | :--------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------------------------------- |
|
||||
| **Shell** | 启动器 (Launcher) | 1. 负责 CEP 扩展的安装与注册<br>2. 负责用户登录鉴权<br>3. **下载并解压** Core 包<br>4. 跳转加载 Core | **本地用户电脑**<br>(安装在 PS 扩展目录) | 极低<br>(仅当下载逻辑变更时更新) |
|
||||
| **Core** | 业务核 (Business) | 1. 包含所有 Vue 业务页面<br>2. 包含核心 JSX 脚本<br>3. 通过 Shell 注入的变量与宿主交互 | **远程服务器**<br>(ZIP 压缩包) | 极高<br>(随时发布新功能) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 目录结构改造建议
|
||||
|
||||
目前的 `Designer/` 目录将作为**核心业务仓库**,我们需要在其中增加一个专门构建 Shell 的入口。
|
||||
|
||||
```
|
||||
Designer/
|
||||
├── package.json
|
||||
├── vite.config.ts # [Core配置] 构建业务代码 (输出 dist/core)
|
||||
├── vite.shell.config.ts # [Shell配置] 构建启动器代码 (输出 dist/shell)
|
||||
│
|
||||
├── src/
|
||||
│ ├── main.ts # [Core入口] 业务主入口 (现在的代码)
|
||||
│ ├── App.vue # [Core] 业务主界面
|
||||
│ │
|
||||
│ └── launcher/ # [Shell] 启动器模块 (NEW)
|
||||
│ ├── main.ts # [Shell入口]
|
||||
│ ├── App.vue # [Shell] 登录与更新进度条界面
|
||||
│ ├── updater.ts # [Shell] 下载/解压/校验逻辑 (Node.js)
|
||||
│ └── login.ts # [Shell] 登录逻辑
|
||||
│
|
||||
└── dist/
|
||||
├── shell/ # 打包结果:这是给用户安装的 ZXP
|
||||
│ ├── CSXS/manifest.xml
|
||||
│ ├── index.html (登录器)
|
||||
│ └── node_modules (含 fs/adm-zip 等必要依赖)
|
||||
│
|
||||
└── core/ # 打包结果:这是传到服务器的 UPDATE.zip
|
||||
├── index.html (业务界面)
|
||||
├── assets/
|
||||
└── jsx/ (加密后的 JSX)
|
||||
```
|
||||
|
||||
## 3. 核心运行流程 (Workflow)
|
||||
|
||||
### 3.1 启动阶段 (Shell)
|
||||
|
||||
1. 用户点击 PS 菜单,加载本地的 `dist/shell/index.html`。
|
||||
2. **Shell App** 启动,不再加载复杂的业务组件,只显示登录框。
|
||||
3. 用户输入账号密码 -> 请求服务器 `/api/login`。
|
||||
4. 登录成功 -> 获取 Token 和 **最新 Core 版本号** (比如 `v1.0.2`)。
|
||||
|
||||
### 3.2 更新阶段 (Updater)
|
||||
|
||||
1. 检查本地 `C:/Users/xxx/AppData/Roaming/DesignerCache/v1.0.2/` 是否存在。
|
||||
2. **不存在 (有更新)**:
|
||||
- 显示进度条。
|
||||
- 从 CDN/服务器下载 `core-v1.0.2.zip`。
|
||||
- 使用 Node.js (`adm-zip`) 解压到上述目录。
|
||||
3. **存在**: 直接跳过。
|
||||
|
||||
### 3.3 注入阶段 (Launch)
|
||||
|
||||
1. **加载 JSX**:
|
||||
- Shell 调用 `CSInterface.evalScript`。
|
||||
- 读取缓存目录下的 `v1.0.2/jsx/index.jsx` 内容。
|
||||
- 执行 `$.evalFile("C:/.../v1.0.2/jsx/index.jsx")`,把业务函数注入此 ExtendScript 上下文。
|
||||
2. **加载 UI**:
|
||||
- Shell 执行 `window.location.href = "file:///C:/.../v1.0.2/index.html"`。
|
||||
3. **接管**:
|
||||
- 浏览器跳转,页面变成业务界面。
|
||||
- 此时 `window.cep` 对象依然存在,业务界面可以继续调用刚刚注入的 JSX 函数。
|
||||
|
||||
## 4. 兼容性评估
|
||||
|
||||
**现有框架可以完全兼容,只需做以下调整**:
|
||||
|
||||
1. **Vite 配置拆分**: 需要新增一个 `vite.shell.config.ts`,专门用来打包轻量级的 Shell。
|
||||
2. **Node.js 依赖**: Shell 需要打包进 `adm-zip` (解压用) 和 `axios` (下载用)。由于 CEP 支持 Node.js,这完全没问题。
|
||||
3. **路由模式**: 业务代码 (Core) 读取本地文件时,路由最好使用 `Hash` 模式 (`createWebHashHistory`),避免 file 协议下的路径问题。
|
||||
|
||||
## 5. 下一步行动计划
|
||||
|
||||
1. **[Shell]** 创建 `src/launcher` 目录,编写简单的登录+下载器 Demo。
|
||||
2. **[Build]** 配置 `vite.shell.config.ts`,尝试打包出一个只包含登录功能的 ZXP。
|
||||
3. **[Test]** 模拟远程更新,手动把现在的业务代码打包成 ZIP 放在本地服务器,测试 Shell 能否下载并跳转。
|
||||
89
temp_backup/Server_redundant/tempdocs/psmark脚本分析报告.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# psmark 脚本分析与功能报告
|
||||
|
||||
## 1. 概述
|
||||
|
||||
在 `psmark` 目录下发现的 `JSX*.py` 文件(JSX2.py - JSX9.py)主要承担了**Photoshop 自动化脚本容器**的角色。
|
||||
这些 Python 文件本身不包含图像处理逻辑,而是作为 Adobe ExtendScript (JSX) 代码的“包装壳”。所有的核心自动化逻辑(如图层操作、裁片缩放、排版等)都由嵌入的 JSX 字符串实现。
|
||||
|
||||
## 2. 文件功能详细清单
|
||||
|
||||
| 文件名 | 核心功能 | 详细描述 |
|
||||
| :----------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **JSX2.py** | **裁片射出宽高缩放** | - 遍历文档图层,根据图层名称(包含尺寸信息)进行精确缩放。<br>- 包含像素与毫米的自动换算。<br>- 处理 90 度/180 度 旋转逻辑。<br>- 使用 `ActionManager` 进行高效的选区和变换操作。 |
|
||||
| **JSX3.py** | **设置花样组顺序居中** | - 专用于印花对位。<br>- 自动识别 "P" 开头的花样图层。<br>- 将花样层对齐到“大货裁片”组的中心位置。<br>- 包含蒙版选区获取和坐标计算逻辑。 |
|
||||
| **JSX4.py** | **图像切割** | - 根据特定图层的边界对文档进行裁剪。<br>- 包含出血/边距处理(例如自动从选区扩展一定像素)。<br>- 适用于从大图中提取独立裁片。 |
|
||||
| **JSX5.py** | **图像切割 (变体)** | - 功能与 JSX4 类似,侧重于特定图层组的裁剪和蒙版应用。 |
|
||||
| **JSX6.py** | **裁片射出宽高缩放 (变体)** | - 与 JSX2 类似,提供裁片缩放和定位功能。<br>- 强调了“前景色修改”和“图层选择”等辅助操作。 |
|
||||
| **JSX7.py** | **裁片射出缩放模板** | - **关键特性**:引入了“**缩放定位点**”图层概念。<br>- 不以图层中心为原点,而是根据“缩放定位点”图层的选区中心进行缩放变换。<br>- 包含 `烧花线添加` 功能,用于绘制剪口和标记线。 |
|
||||
| **JSX8.py** | **批量化替换外链新 (UI)** | - **全自动化工作流脚本**。<br>- **内置 UI**:包含一个 ScriptUI 编写的对话框,用于选择文件夹路径。<br>- **功能**:<br> 1. 批量打开文件。<br> 2. 自动替换智能对象链接(换花型)。<br> 3. `码标添加`:自动生成包含文件名的尺寸标记。<br> 4. `花样标准化`:统一花样大小并添加白底。<br> 5. 自动另存为 TIF 格式并归档。 |
|
||||
| **JSX9.py** | **按中心点比例缩放** | - 结合了 JSX2 和 JSX7 的特性。<br>- 专注于按“缩放定位点”进行**等比例**或**宽高独立**缩放。<br>- 包含详细的 `ActionManager` 变换代码(`transform` 命令)。 |
|
||||
| **JSX16.py** | **花样图层导出** | - 遍历图层组(特别是“填充底图”),将子图层导出为 TIF。<br>- 处理“最大白边值”图层以确定裁剪边界。<br>- 包含智能对象转换、重新链接、蒙版合并等复合操作。 |
|
||||
| **JSX17.py** | **定位码批量快速换图 (UI)** | - **带 ScriptUI**。<br>- 全自动批量流程:选择模板路径、素材路径、输出路径。<br>- 遍历素材图片,打开模板,替换智能对象(`placedLayerRelinkToFile`),应用预设图案填充,合并图层并另存。<br>- 支持读取 JSON 数据来匹配图案名称。 |
|
||||
| **JSX18.py** | **龙服快速换图 (UI)** | - **带 ScriptUI**(定制版本)。<br>- 读取文件夹文件列表,允许用户在 UI 中输入“数量”信息。<br>- 根据输入信息更新图层文本内容,替换裁片智能对象链接,合并并保存。 |
|
||||
| **JSX19.py** | **裁片抓取与导入** | - 批量置入 PDF 文件。<br>- **自动旋转特性**:通过解析文件名(如包含 `_180`)自动判断是否需要旋转 180 度导入。<br>- 自动建立“大货裁片”组并归档。 |
|
||||
| **JSX20.py** | **混合裁片导出** | - 遍历包含“大货裁片”名称的图层组。<br>- 逐个提取子图层,应用蒙版,裁切到边界,导出为 TIF 文件。<br>- 使用历史记录回退(`historyState`)来恢复状态以处理下一个图层。 |
|
||||
| **JSX21.py** | **裁片排版基础** | - 基础排版功能库。<br>- `创建裁片排版文档`:按毫米创建新画布。<br>- `置入链接的智能对象`:将外部文件作为链接对象置入。<br>- `裁片排版_lay`:提供基于毫米坐标的精确位移功能。 |
|
||||
| **JSX22.py** | **模特换衣功能 (UI)** | - **带 ScriptUI**。<br>- 针对模特展示图的批量替换。<br>- 遍历素材目录,替换模板中名为“替换对象”的智能对象。<br>- 支持保持原始目录结构导出,支持切片导出(Web 切片)。 |
|
||||
| **JSX23.py** | **S/O 样自动连晒 (UI)** | - **带 ScriptUI**。<br>- 自动生成连晒(米样)效果。<br>- 将图片定义为图案,填充到指定宽高的文档中(平铺),并添加对应的文件名文字标签。<br>- 保存两份:一份纯拼贴,一份带文字标签。 |
|
||||
| **JSX24.py** | **自动米样拼贴 (UI)** | - **带 ScriptUI**。<br>- 与 JSX23 类似但逻辑不同,这是真正的**拼图**(Collage)。<br>- 读取文件夹所有图片,按高度排序。<br>- 自动计算总画布大小,将图片紧凑排列(换行逻辑)拼贴到一个大文档中。 |
|
||||
| **JSX26.py** | **模特多色换图** | - 利用**快照**(Snapshot)机制进行批量处理。<br>- 遍历文件夹素材,替换“贴图位置”组内的图层内容。<br>- 支持对替换后的图案进行位移(Offset)以制作不同花位效果。<br>- 导出 TIF 后回退到快照状态。 |
|
||||
| **JSX27.py** | **新的米样缩放 (UI)** | - **带 ScriptUI**。<br>- 批量调整图片尺寸到指定的厘米数(Resize Image)。<br>- 随后进行画布扩展和文字标签添加,用于制作标准化的缩放样图。 |
|
||||
|
||||
## 3. 技术架构特点
|
||||
|
||||
### 3.1 Python 包装器模式
|
||||
|
||||
实际上是“伪 Python”代码。
|
||||
|
||||
```python
|
||||
# 典型结构
|
||||
dxf7_jscode = """
|
||||
function 核心功能() {
|
||||
// 实际的 ExtendScript (JavaScript) 代码
|
||||
var doc = app.activeDocument;
|
||||
...
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
这种结构是为了便于 Python 后端(可能是旧的自动化系统)直接将 JS 代码发送给 Photoshop 执行。迁移时应直接提取 `"""` 内部的内容。
|
||||
|
||||
### 3.2 ActionManager (AM) 代码
|
||||
|
||||
脚本极度依赖 Photoshop 的底层 ActionManager API(`executeAction`, `ActionDescriptor`)。
|
||||
|
||||
- **优点**:执行速度快,能实现 DOM API(普通 JS 对象)无法实现的功能(如“再次变换”、“应用图层样式”、“特定算法的选区运算”)。
|
||||
- **缺点**:代码可读性差,维护难度大(充斥着 `stringIDToTypeID`)。
|
||||
|
||||
### 3.3 命名约定
|
||||
|
||||
脚本逻辑强依赖于图层和组的命名规范:
|
||||
|
||||
- `P...`:识别为花样图层。
|
||||
- `-大货裁片`:识别为标准的裁片组模板。
|
||||
- `缩放定位点`:用于计算变换中心的辅助图层。
|
||||
- `_` 分隔符:用于从图层名中提取尺寸参数(如 `名称_宽度_高度`)。
|
||||
|
||||
## 4. 迁移与集成建议 (DesignerCEP)
|
||||
|
||||
为了将这些资产整合到当前的 `DesignerCEP` (Vue + TypeScript + CEP) 项目中,建议采取以下步骤:
|
||||
|
||||
1. **代码提取 (Extract)**:
|
||||
|
||||
- 废弃 `.py` 文件。
|
||||
- 将字符串内的 JS 代码提取并在 `src/jsx/` 目录下建立对应的 `.jsx` 文件(建议按功能重命名,如 `ResizeUtils.jsx`, `BatchProcess.jsx`)。
|
||||
|
||||
2. **模块化重构 (Refactor)**:
|
||||
|
||||
- 原脚本大量使用全局变量,容易造成污染。需要将其封装为独立的函数,例如 `function scaleLayerByAnchor(layerName, anchorLayerName) { ... }`。
|
||||
- 将通用的 `ActionManager` 辅助函数(如 `executeAction` 的封装)移动到统一的工具库中。
|
||||
|
||||
3. **UI 重写 (Modernize UI)**:
|
||||
|
||||
- **重点**:`JSX8.py` 中的 ScriptUI 弹窗(灰色原生界面)应完全废弃。
|
||||
- 使用 **Vue + Element Plus/Arco Design** 重写批量处理界面。
|
||||
- 前端收集用户输入的路径和选项,通过 `cep.ts` 桥接层传递给 JSX 执行核心逻辑。
|
||||
|
||||
4. **功能桥接 (Bridge)**:
|
||||
- 在 `src/jsx/index.ts` 中暴露新的接口,供前端调用。
|
||||
- 例如:`canvas_cut_image` (对应 JSX4), `auto_resize_layer` (对应 JSX2)。
|
||||
86
temp_backup/Server_redundant/tempdocs/zhihu_articles.json
Normal file
@@ -0,0 +1,86 @@
|
||||
[
|
||||
{
|
||||
"title": "【CEP教程-17】插件的打包和发布",
|
||||
"url": "https://zhuanlan.zhihu.com/p/27361054277"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-16】JSX的工程化",
|
||||
"url": "https://zhuanlan.zhihu.com/p/22605290525"
|
||||
},
|
||||
{
|
||||
"title": "【UXP教程-2】UXP插件开发起步",
|
||||
"url": "https://zhuanlan.zhihu.com/p/20904402159"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-15】前端框架在插件面板中的应用",
|
||||
"url": "https://zhuanlan.zhihu.com/p/683712943"
|
||||
},
|
||||
{
|
||||
"title": "我给三年级女儿开发了一个打字网站",
|
||||
"url": "https://zhuanlan.zhihu.com/p/676856628"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-14】数据存储相关",
|
||||
"url": "https://zhuanlan.zhihu.com/p/675795467"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-13】nodejs在插件开发中的应用",
|
||||
"url": "https://zhuanlan.zhihu.com/p/661392566"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-12】如何从Ps中导出图片",
|
||||
"url": "https://zhuanlan.zhihu.com/p/658067352"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-11】生成器",
|
||||
"url": "https://zhuanlan.zhihu.com/p/643541900"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-10】图层处理那些事",
|
||||
"url": "https://zhuanlan.zhihu.com/p/617477492"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-10】Action Manager完全指南 - 下篇",
|
||||
"url": "https://zhuanlan.zhihu.com/p/608104095"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-9】Action Manager完全指南 - 中篇",
|
||||
"url": "https://zhuanlan.zhihu.com/p/601014597"
|
||||
},
|
||||
{
|
||||
"title": "【Adobe UXP插件开发中文教程】- 1. 简介",
|
||||
"url": "https://zhuanlan.zhihu.com/p/600569875"
|
||||
},
|
||||
{
|
||||
"title": "【CEP教程-8】Action Manager完全指南 - 上篇",
|
||||
"url": "https://zhuanlan.zhihu.com/p/600014746"
|
||||
},
|
||||
{
|
||||
"title": "Photoshop插件开发教程 - (7)JSX脚本指南 - DOM篇",
|
||||
"url": "https://zhuanlan.zhihu.com/p/596166382"
|
||||
},
|
||||
{
|
||||
"title": "Photoshop插件开发教程 - (6)面板与宿主之间的交互",
|
||||
"url": "https://zhuanlan.zhihu.com/p/566983957"
|
||||
},
|
||||
{
|
||||
"title": "Photoshop插件开发教程 - (5)插件面板的样式",
|
||||
"url": "https://zhuanlan.zhihu.com/p/563847844"
|
||||
},
|
||||
{
|
||||
"title": "Photoshop插件开发教程 - (4)开发工具选择和调试",
|
||||
"url": "https://zhuanlan.zhihu.com/p/559290141"
|
||||
},
|
||||
{
|
||||
"title": "Photoshop插件开发教程 - (3)CEP插件面板结构介绍",
|
||||
"url": "https://zhuanlan.zhihu.com/p/555070606"
|
||||
},
|
||||
{
|
||||
"title": "Photoshop插件开发教程 - (2)开发环境搭建",
|
||||
"url": "https://zhuanlan.zhihu.com/p/532152091"
|
||||
},
|
||||
{
|
||||
"title": "Photoshop插件开发教程 - (1)插件类型",
|
||||
"url": "https://zhuanlan.zhihu.com/p/518229060"
|
||||
}
|
||||
]
|
||||
44
temp_backup/Server_redundant/tempdocs/修复命令.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# 🔧 服务器修复命令
|
||||
|
||||
## 在服务器执行以下命令:
|
||||
|
||||
```bash
|
||||
# 1. 创建日志目录
|
||||
mkdir -p /var/log/caddy
|
||||
|
||||
# 2. 设置权限(让 Caddy 能写入)
|
||||
chown -R caddy:caddy /var/log/caddy
|
||||
chmod 755 /var/log/caddy
|
||||
|
||||
# 3. 重新上传 docker-compose.yml(已修改端口映射)
|
||||
# 在本地执行:
|
||||
# scp Server/docker-compose.yml root@103.97.201.136:/root/server/
|
||||
|
||||
# 4. 重新上传 Caddyfile(已修改反向代理)
|
||||
# 在本地执行:
|
||||
# scp Caddyfile root@103.97.201.136:/etc/caddy/Caddyfile
|
||||
|
||||
# 5. 重启 Docker 容器(应用端口映射)
|
||||
cd /root/server
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
|
||||
# 6. 启动 Caddy
|
||||
systemctl restart caddy
|
||||
|
||||
# 7. 查看状态
|
||||
systemctl status caddy
|
||||
docker-compose ps
|
||||
|
||||
# 8. 测试
|
||||
curl http://localhost:8000/health
|
||||
curl -I http://localhost/
|
||||
```
|
||||
|
||||
## ✅ 全部成功后访问
|
||||
|
||||
```
|
||||
https://app.aidg168.uk/
|
||||
https://backend.aidg168.uk/health
|
||||
```
|
||||
|
||||
131
temp_backup/Server_redundant/tempdocs/修复说明.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# ✅ 修复完成:使用 JSON 文件替代 MySQL
|
||||
|
||||
## 🔧 问题与解决
|
||||
|
||||
### 原问题
|
||||
启动时弹出错误:`Can't connect to MySQL server on 'localhost'`
|
||||
|
||||
### 根本原因
|
||||
- MySQL 运行在 Docker 容器中,端口未映射到宿主机
|
||||
- 远程 MySQL 连接复杂,需要 SSH 隧道
|
||||
|
||||
### 最终方案 ✅
|
||||
**使用 SSH + JSON 文件存储版本信息**
|
||||
|
||||
- ✅ 不需要配置 MySQL
|
||||
- ✅ 不需要额外的库(sshtunnel, pymysql)
|
||||
- ✅ 更简单、更可靠
|
||||
- ✅ 版本信息存储在服务器:`/var/www/app_versions/.deployments.json`
|
||||
|
||||
---
|
||||
|
||||
## 📦 当前依赖(精简版)
|
||||
|
||||
```
|
||||
PyQt5 # GUI 界面
|
||||
requests # API 请求
|
||||
paramiko # SSH 连接
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 数据存储方式
|
||||
|
||||
### JSON 文件位置
|
||||
```
|
||||
/var/www/app_versions/.deployments.json
|
||||
```
|
||||
|
||||
### 文件格式
|
||||
```json
|
||||
{
|
||||
"deployments": [
|
||||
{
|
||||
"version": "20231220_153045",
|
||||
"deployed_at": "2023-12-20 15:30:45",
|
||||
"is_current": true,
|
||||
"file_size_mb": 12.5,
|
||||
"comment": "修复主题同步bug"
|
||||
},
|
||||
{
|
||||
"version": "20231220_102030",
|
||||
"deployed_at": "2023-12-20 10:20:30",
|
||||
"is_current": false,
|
||||
"file_size_mb": 12.3,
|
||||
"comment": "初始版本"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 现在可以正常使用
|
||||
|
||||
### 1. 启动工具
|
||||
```bash
|
||||
cd AdminTool
|
||||
python admin_gui.py
|
||||
```
|
||||
|
||||
**不会再弹出数据库错误!** ✅
|
||||
|
||||
### 2. 测试系统
|
||||
```bash
|
||||
cd AdminTool
|
||||
python test_version_system.py
|
||||
```
|
||||
|
||||
应该看到:
|
||||
```
|
||||
✅ 测试完成!版本管理系统一切正常
|
||||
```
|
||||
|
||||
### 3. 开始部署
|
||||
1. 构建前端:`cd Designer && npm run build:core`
|
||||
2. 打开 GUI,切换到"自动化部署"
|
||||
3. 选择 `dist_core` 目录
|
||||
4. 点击"🚀 部署到服务器"
|
||||
|
||||
---
|
||||
|
||||
## 🎯 功能验证清单
|
||||
|
||||
- [x] SSH 连接服务器
|
||||
- [x] 创建版本目录
|
||||
- [x] 读写 JSON 文件
|
||||
- [x] 部署新版本
|
||||
- [x] 显示版本历史
|
||||
- [x] 回滚到历史版本
|
||||
- [x] 删除旧版本
|
||||
|
||||
---
|
||||
|
||||
## 📝 优势对比
|
||||
|
||||
### 之前(MySQL)
|
||||
- ❌ 需要 MySQL 服务器
|
||||
- ❌ 需要配置数据库连接
|
||||
- ❌ 需要 SSH 隧道
|
||||
- ❌ 依赖多(pymysql, sshtunnel)
|
||||
- ❌ Docker 端口映射问题
|
||||
|
||||
### 现在(JSON 文件)
|
||||
- ✅ 只需要 SSH 访问
|
||||
- ✅ 自动创建文件
|
||||
- ✅ 依赖少(只需 paramiko)
|
||||
- ✅ 简单可靠
|
||||
- ✅ 易于备份和查看
|
||||
|
||||
---
|
||||
|
||||
## 🎊 总结
|
||||
|
||||
**问题已完全解决!** 现在可以:
|
||||
1. 正常启动 GUI(不会弹窗)
|
||||
2. 部署新版本
|
||||
3. 管理版本历史
|
||||
4. 随时回滚
|
||||
|
||||
**下一步**:开始使用部署功能!
|
||||
|
||||
412
temp_backup/Server_redundant/tempdocs/全量接口文档.md
Normal file
@@ -0,0 +1,412 @@
|
||||
# Server API 全量文档
|
||||
|
||||
## 概述
|
||||
|
||||
- **基础地址**:`http://localhost:8000`
|
||||
- **版本前缀**:`/api/v1`
|
||||
- **认证方式**:
|
||||
- Header: `Authorization: Bearer <access_token>` (用于普通用户接口)
|
||||
- Header: `x-admin-token: admin-secret-token` (用于管理员接口)
|
||||
- Header: `x-api-key: <key>` (用于部分工具接口)
|
||||
|
||||
---
|
||||
|
||||
## 📚 1. 认证模块 (Auth)
|
||||
|
||||
**Base Path**: `/api/v1/auth`
|
||||
|
||||
### 1.1 注册
|
||||
|
||||
- **URL**: `POST /register`
|
||||
- **功能**: 用户注册
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"password": "password123",
|
||||
"confirm_password": "password123",
|
||||
"email": "user1@example.com", // 可选
|
||||
"code": "123456", // 验证码(可选)
|
||||
"device_id": "device_001" // 可选,默认 unknown_device
|
||||
}
|
||||
```
|
||||
|
||||
- **响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbG...",
|
||||
"token_type": "bearer",
|
||||
"username": "user1"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 登录
|
||||
|
||||
- **URL**: `POST /login`
|
||||
- **功能**: 用户登录,获取 Token
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"password": "password123",
|
||||
"device_id": "device_001"
|
||||
}
|
||||
```
|
||||
|
||||
- **响应**: 同注册接口
|
||||
|
||||
### 1.3 发送验证码
|
||||
|
||||
- **URL**: `POST /send-verification-code`
|
||||
- **功能**: 发送注册/验证邮件验证码
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user1@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 验证邮箱
|
||||
|
||||
- **URL**: `POST /verify-email`
|
||||
- **功能**: 验证邮箱验证码
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"code": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.5 忘记密码
|
||||
|
||||
- **URL**: `POST /forgot-password`
|
||||
- **功能**: 发送重置密码邮件
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user1@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.6 重置密码
|
||||
|
||||
- **URL**: `POST /reset-password`
|
||||
- **功能**: 使用 Token 重置密码
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user1@example.com",
|
||||
"token": "123456",
|
||||
"new_password": "newpassword123",
|
||||
"confirm_password": "newpassword123"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.7 登出
|
||||
|
||||
- **URL**: `POST /logout`
|
||||
- **功能**: 退出当前设备登录
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"device_id": "device_001"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.8 许可证验证 (Verify)
|
||||
|
||||
- **URL**: `POST /verify`
|
||||
- **功能**: 验证当前 Token 和会话是否有效(用于应用启动检查)
|
||||
- **Headers**: `Authorization: Bearer <token>`
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"device_id": "device_001"
|
||||
}
|
||||
```
|
||||
|
||||
- **响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": true,
|
||||
"username": "user1",
|
||||
"expire_date": "2025-12-31T23:59:59" // 若有过期时间
|
||||
}
|
||||
```
|
||||
|
||||
### 1.9 心跳 (Heartbeat)
|
||||
|
||||
- **URL**: `POST /heartbeat`
|
||||
- **功能**: 维持会话活跃状态
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"device_id": "device_001"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.10 获取在线时长
|
||||
|
||||
- **URL**: `GET /online-time/{username}`
|
||||
- **功能**: 获取用户累计在线时长
|
||||
- **响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"total_seconds": 3600, // 历史累计
|
||||
"active_seconds": 120 // 当前会话
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🖥️ 2. 客户端模块 (Client)
|
||||
|
||||
**Base Path**: `/api/v1/client`
|
||||
|
||||
### 2.1 检查更新
|
||||
|
||||
- **URL**: `POST /check_update`
|
||||
- **功能**: 检查插件是否有新版本
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1" // 用于检查用户所在组的特定版本
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 客户端登录
|
||||
|
||||
- **URL**: `POST /login`
|
||||
- **功能**: 客户端专用登录,返回更多用户信息
|
||||
- **请求体**: 同 Auth 登录
|
||||
- **响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"token": "eyJ...",
|
||||
"username": "user1",
|
||||
"expire_date": "2025-12-31",
|
||||
"permissions": ["plugin.use"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👤 3. 用户资料 (User Profile)
|
||||
|
||||
**Base Path**: `/api/v1`
|
||||
|
||||
### 3.1 获取资料
|
||||
|
||||
- **URL**: `GET /user/profile?username=user1`
|
||||
- **功能**: 获取用户详细资料(积分、VIP 状态、签到信息等)
|
||||
- **Headers**: `Authorization: Bearer <token>`
|
||||
|
||||
### 3.2 更新资料
|
||||
|
||||
- **URL**: `PUT /user/profile`
|
||||
- **功能**: 更新昵称、头像等
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"nickname": "New Nickname",
|
||||
"avatar": "http://example.com/avatar.jpg"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 积分历史
|
||||
|
||||
- **URL**: `GET /points/history`
|
||||
- **功能**: 分页获取积分变动记录
|
||||
- **Query Params**: `username`, `type` (可选: checkin/consume/reward), `page`, `limit`
|
||||
|
||||
---
|
||||
|
||||
## 📅 4. 签到模块 (Check-In)
|
||||
|
||||
**Base Path**: `/api/v1/checkin`
|
||||
|
||||
### 4.1 每日签到
|
||||
|
||||
- **URL**: `POST /daily`
|
||||
- **功能**: 执行每日签到
|
||||
- **请求体**: `{"username": "user1"}`
|
||||
- **响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"success": true,
|
||||
"points_earned": 10,
|
||||
"consecutive_days": 5,
|
||||
"message": "签到成功..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 签到状态
|
||||
|
||||
- **URL**: `GET /status?username=user1`
|
||||
- **功能**: 检查今日是否已签到
|
||||
|
||||
### 4.3 签到日历
|
||||
|
||||
- **URL**: `GET /calendar/{year}/{month}`
|
||||
- **功能**: 获取指定月份的签到日期列表
|
||||
- **Query Params**: `username`
|
||||
|
||||
### 4.4 签到记录 (列表)
|
||||
|
||||
- **URL**: `GET /history?username=user1&page=1`
|
||||
- **功能**: 分页获取签到记录
|
||||
|
||||
### 4.5 获取签到规则
|
||||
|
||||
- **URL**: `GET /config`
|
||||
- **功能**: 获取签到奖励规则(公开接口,无需 Admin Token)
|
||||
- **响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": [
|
||||
{
|
||||
"consecutive_days": 1,
|
||||
"base_points": 10,
|
||||
"bonus_points": 0,
|
||||
"total_points": 10
|
||||
},
|
||||
{
|
||||
"consecutive_days": 7,
|
||||
"base_points": 10,
|
||||
"bonus_points": 20,
|
||||
"total_points": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 5. 功能使用 (Feature)
|
||||
|
||||
**Base Path**: `/api/v1/feature`
|
||||
|
||||
### 5.1 使用功能的 (扣费接口)
|
||||
|
||||
- **URL**: `POST /use`
|
||||
- **功能**: 记录功能使用,扣除积分或 VIP 配额
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"feature_key": "ai_remove_bg",
|
||||
"device_id": "device_001"
|
||||
}
|
||||
```
|
||||
|
||||
- **响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"success": true,
|
||||
"cost_type": "points", // 或 vip_quota, free
|
||||
"points_cost": 10,
|
||||
"message": "消耗10积分"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧮 6. 工具演示 (JSX Demo)
|
||||
|
||||
**Base Path**: `/api/v1/jsx_demo`
|
||||
|
||||
### 6.1 计算表达式
|
||||
|
||||
- **URL**: `POST /calculate`
|
||||
- **Headers**: `x-api-key: <optional>`
|
||||
- **请求体**: `{"expression": "1+1"}`
|
||||
|
||||
---
|
||||
|
||||
## 📊 7. 统计与日志 (Analytics & Stats)
|
||||
|
||||
**Base Path**: `/api/v1`
|
||||
|
||||
### 7.1 上报日志
|
||||
|
||||
- **URL**: `POST /analytics/log`
|
||||
- **功能**: 客户端上报行为日志
|
||||
- **请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"device_id": "dev1",
|
||||
"action": "click_button",
|
||||
"timestamp": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 获取用户统计
|
||||
|
||||
- **URL**: `GET /analytics/stats/{username}`
|
||||
|
||||
---
|
||||
|
||||
## 👑 8. 管理员后台 (Admin)
|
||||
|
||||
**Headers**: `x-admin-token: admin-secret-token` (部分接口兼容 Form 表单 token)
|
||||
|
||||
### 8.1 基础管理 (Base Path: `/api/v1/admin`)
|
||||
|
||||
- `POST /upload_version`: 上传新版本插件包
|
||||
- `GET /archives`: 列出历史版本
|
||||
- `POST /groups`: 创建用户组
|
||||
- `GET /groups`: 获取用户组列表
|
||||
- `PUT /groups/{id}`: 更新用户组
|
||||
- `GET /users`: 获取所有用户列表
|
||||
- `PUT /users/{id}/group`: 修改用户所属组
|
||||
- `PUT /users/{id}/permissions`: 修改用户权限
|
||||
|
||||
### 8.2 配置管理 (Base Path: `/api/v1/admin/config`)
|
||||
|
||||
- **功能配置** (`/features`): GET(列表), POST(新增), PUT(/{key} 更新), DELETE(/{key} 删除)
|
||||
- **VIP 配置** (`/vip`): GET(列表), PUT(/{type} 更新)
|
||||
- **签到配置** (`/checkin`): GET(列表), POST(新增), PUT(/{days} 更新), DELETE(/{days} 删除)
|
||||
|
||||
### 8.3 数据统计 (Base Path: `/api/v1/admin/stats`)
|
||||
|
||||
- `GET /today`: 今日概览 (用户数、签到数、功能使用数)
|
||||
- `GET /feature-usage`: 功能使用排行 (Top 10)
|
||||
- `GET /points-trend`: 积分收支趋势 (近 7 天)
|
||||
82
temp_backup/Server_redundant/tempdocs/前端代码结构与功能说明.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# 前端代码结构与功能说明 (DesignerCEP)
|
||||
|
||||
本文档详细说明了 `DesignerCEP` 项目的前端代码结构,包括新引入的 **"Shell (启动器) + Core (业务核)"** 双层架构。
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心架构说明
|
||||
|
||||
项目被设计为两个独立的部分,分别构建:
|
||||
|
||||
1. **Shell (启动器)**:
|
||||
- **职责**: 极轻量级,负责登录验证、检查版本、下载 ZIP、解压、动态加载 Core。
|
||||
- **代码位置**: `src/launcher/`
|
||||
- **构建产物**: 生成 ZXP 安装包的主体。
|
||||
2. **Core (业务核)**:
|
||||
- **职责**: 包含所有设计功能(Vue 界面 + JSX 脚本)。
|
||||
- **代码位置**: `src/` (除 `launcher` 外)
|
||||
- **构建产物**: 被打包成 ZIP,由 Shell 下载并运行。
|
||||
|
||||
---
|
||||
|
||||
## 2. 目录结构详解
|
||||
|
||||
### 2.1 根目录配置
|
||||
|
||||
| 文件名 | 作用 |
|
||||
| :--------------------- | :------------------------------------------------------------------------------------------------------------ |
|
||||
| `package.json` | 项目依赖管理。包含 `npm run build:shell` (构建启动器) 和 `npm run build:core` (构建业务核) 等脚本。 |
|
||||
| `vite.config.ts` | **Core 的构建配置**。用于开发环境 (`npm run dev`) 和业务核打包。配置了 API 代理和 Vue 插件。 |
|
||||
| `vite.shell.config.ts` | **Shell 的构建配置**。专门用于构建启动器,指定入口为 `src/launcher/index.html`,并打包为独立的 CEP 插件结构。 |
|
||||
| `prod.cep.config.ts` | CEP 插件的生产环境配置文件 (Manifest)。定义包括插件 ID、版本、窗口大小、支持的宿主 (PS, AI 等)。 |
|
||||
| `tsconfig.json` | TypeScript 配置文件。定义了别名 (`@/`, `@plugins/`) 和编译选项。 |
|
||||
|
||||
### 2.2 Shell 启动器 (`src/launcher/`)
|
||||
|
||||
这是用户安装 ZXP 后第一时间运行的代码。
|
||||
|
||||
| 路径/文件名 | 作用 |
|
||||
| :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`index.html`** | Shell 的 HTML 入口。引入 `CSInterface.js` 和 `main.ts`。 |
|
||||
| **`main.ts`** | Shell 的 JS 入口。初始化 Vue 应用,挂载 `App.vue`。 |
|
||||
| **`App.vue`** | Shell 的根组件。包含 `<router-view>`,用于切换登录/更新界面。 |
|
||||
| **`router.ts`** | Shell 的路由配置。定义了 `/login` (登录页) 和 `/register` (注册页)。 |
|
||||
| **`view/Login.vue`** | **核心登录界面**。集成了 `Updater` 类,点击登录后自动执行:**登录 -> 检查更新 -> 下载 ZIP -> 解压 -> 跳转**。 |
|
||||
| **`view/Register.vue`** | 注册界面。 |
|
||||
| **`utils/updater.ts`** | **核心更新逻辑**。包含 `Updater` 类。负责调用后端 API (`check_update`),使用 `fs` 写入文件,使用 `adm-zip` 解压,最后修改 `window.location.href` 加载 Core。 |
|
||||
| **`jsx/index.ts`** | Shell 的最小化 JSX 脚本。CEP 插件必须包含至少一个 JSX,这里仅返回版本号,不包含重型业务逻辑。 |
|
||||
|
||||
### 2.3 Core 业务核 (`src/`)
|
||||
|
||||
这是实际的业务软件,被下载后动态加载。
|
||||
|
||||
| 路径/目录 | 作用 |
|
||||
| :------------------------------ | :----------------------------------------------------------------------------------------------------- |
|
||||
| **`main.ts`** | Core 的入口文件。引入 Arco Design,全局样式等。 |
|
||||
| **`App.vue`** | Core 的根组件。 |
|
||||
| **`router/index.ts`** | Core 的路由配置。包含 `/home`, `/about` 等业务页面。(注意:已移除登录/注册路由,因为这由 Shell 接管)。 |
|
||||
| **`view/Home.vue`** | 主页组件。包含主要的功能入口和测试按钮。 |
|
||||
| **`jsx/`** | **Adobe ExtendScript (JSX) 脚本目录**。 |
|
||||
|   `index.ts` | JSX 入口。导出所有供 Vue 调用的 Photoshop 接口函数。 |
|
||||
|   `utils/ActionManager.ts` | action manager 底层封装 (如载入选区、填充、新建文档)。底层核心。 |
|
||||
|   `utils/LayerUtils.ts` | 图层操作封装 (如遍历、查找、重命名)。 |
|
||||
| **`utils/cep.ts`** | `CSInterface` 的 TS 封装。用于前端 Vue 调用后端 JSX (`evalScript`)。 |
|
||||
| **`utils/request.ts`** | 基于 Umi-Request 或 Axios 的 HTTP 请求封装 (业务用)。 |
|
||||
|
||||
### 2.4 构建插件 (`plugins/`)
|
||||
|
||||
用于辅助 Vite 打包 CEP 环境的代码。
|
||||
|
||||
| 路径/文件名 | 作用 |
|
||||
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`jsx/cepPlugin.ts`** | **构建核心**。Vite 插件。负责:<br>1. 监听 JSX 变化并重编译 (Dev)。<br>2. 生产环境调用 `rollup` 编译 JSX 为 `index.js`。<br>3. 复制 manifest.xml 和 index.html 到 `dist` 目录。 |
|
||||
| **`buildJsx/index.ts`** | 单独的 Rollup 构建配置,专门用于将 `src/jsx/*.ts` 编译成 Photoshop 能跑的 ES3 格式 JS。 |
|
||||
| **`template/`** | 存放 `manifest.xml`, `.debug` 等 CEP 配置文件的模板。 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 开发与发布流程总结
|
||||
|
||||
1. **开发 Shell**: 修改 `src/launcher` -> `npm run build:shell` -> 生成 ZXP。
|
||||
2. **开发 Core**: 修改 `src/view` 或 `src/jsx` -> `npm run build:core` -> 得到业务 ZIP 包。
|
||||
3. **用户视角**: 安装 ZXP -> 启动插件 -> 看到登录框 (Shell) -> 登录并下载最新 Core -> 自动跳转进入业务界面。
|
||||
126
temp_backup/Server_redundant/tempdocs/前端安全升级接入指南.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 前端安全升级接入指南
|
||||
|
||||
为了提高安全性,后端鉴权机制已升级为 **Token + Device Binding + Session Enforcement**。
|
||||
|
||||
**⚠️ 核心变更:所有身份验证相关的接口现在都强制要求携带 `device_id`。**
|
||||
|
||||
前端必须配合进行以下改动,否则接口将返回 `422 Unprocessable Entity` (参数缺失) 或 `401 Unauthorized` (设备不匹配)。
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 接口改造清单
|
||||
|
||||
请检查并修改以下所有接口的调用参数:
|
||||
|
||||
### 1. 登录接口 (Login)
|
||||
* **URL**: `/api/v1/auth/login`
|
||||
* **变更**: 新增必填字段 `device_id`。
|
||||
* **说明**: 之前的文档描述有误,登录接口**必须**传此参数,否则无法建立会话。
|
||||
|
||||
**修改后示例:**
|
||||
```typescript
|
||||
const loginData = {
|
||||
username: "user1",
|
||||
password: "pwd",
|
||||
device_id: getDeviceId() // ✅ 必传
|
||||
};
|
||||
```
|
||||
|
||||
### 2. 注册接口 (Register)
|
||||
* **URL**: `/api/v1/auth/register`
|
||||
* **变更**: 新增必填字段 `device_id`。
|
||||
* **说明**: 注册成功后会自动登录,因此必须绑定当前设备。
|
||||
|
||||
**修改后示例:**
|
||||
```typescript
|
||||
const registerData = {
|
||||
username: "user1",
|
||||
password: "pwd",
|
||||
confirm_password: "pwd",
|
||||
device_id: getDeviceId() // ✅ 必传
|
||||
};
|
||||
```
|
||||
|
||||
### 3. 登出接口 (Logout)
|
||||
* **URL**: `/api/v1/auth/logout`
|
||||
* **变更**: 新增必填字段 `device_id`。
|
||||
* **说明**: 用于精准注销当前设备的会话,而不影响用户在其他设备上的登录状态。
|
||||
|
||||
**修改后示例:**
|
||||
```typescript
|
||||
const logoutData = {
|
||||
username: "user1",
|
||||
device_id: getDeviceId() // ✅ 必传
|
||||
};
|
||||
```
|
||||
|
||||
### 4. 心跳保活接口 (Heartbeat)
|
||||
* **URL**: `/api/v1/auth/heartbeat`
|
||||
* **变更**: 新增必填字段 `device_id`。
|
||||
* **说明**: 用于维持当前设备 Session 的活跃状态。
|
||||
|
||||
**修改后示例:**
|
||||
```typescript
|
||||
const heartbeatData = {
|
||||
username: "user1",
|
||||
device_id: getDeviceId() // ✅ 必传
|
||||
};
|
||||
```
|
||||
|
||||
### 5. 许可证验证接口 (Verify)
|
||||
* **URL**: `/api/v1/auth/verify`
|
||||
* **变更**: 新增必填字段 `device_id`。
|
||||
* **说明**: 后端会校验 Token 中的设备 ID 是否与参数中的设备 ID 一致,且 Session 是否活跃。
|
||||
|
||||
**修改后示例:**
|
||||
```typescript
|
||||
const verifyData = {
|
||||
username: "user1",
|
||||
timestamp: Date.now(),
|
||||
device_id: getDeviceId() // ✅ 必传
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 统一错误处理 (Interceptor)
|
||||
|
||||
由于新的强校验机制,`401 Unauthorized` 可能会在任何接口出现(不仅仅是 Token 过期,也可能是被踢下线)。
|
||||
|
||||
前端拦截器需要统一处理 `401`,强制跳转到登录页。
|
||||
|
||||
**Axios 拦截器示例:**
|
||||
|
||||
```typescript
|
||||
axios.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response && error.response.status === 401) {
|
||||
// 1. 清除本地 Token
|
||||
localStorage.removeItem('access_token');
|
||||
|
||||
// 2. 提示用户
|
||||
const msg = error.response.data.detail || '登录已失效,请重新登录';
|
||||
// Message.error(msg); // Arco Design / Element UI
|
||||
console.error(msg);
|
||||
|
||||
// 3. 跳转登录页
|
||||
// 如果是 Shell 环境,跳转到 Shell 登录
|
||||
if (window.location.hash.indexOf('/login') === -1) {
|
||||
window.location.href = '/shell/index.html#/login';
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 关于 device_id 的说明
|
||||
|
||||
`device_id` 是用于标识客户端设备的唯一字符串。
|
||||
|
||||
* **生成方式**:建议在应用启动时检查 `LocalStorage`,如果没有则生成一个 UUID 并存入;如果有则直接读取。
|
||||
* **作用**:用于区分不同的客户端实例,实现单设备登录限制(互踢功能)。
|
||||
* **持久化**:必须确保存储在浏览器/CEP 环境的持久化存储中(如 `localStorage`),避免刷新页面后变化。
|
||||
160
temp_backup/Server_redundant/tempdocs/后端API接口文档.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# DesignerCEP 后端 API 接口文档
|
||||
|
||||
本文档描述了 DesignerCEP 后端服务的 API 接口规范,包括客户端插件接口和后台管理接口。
|
||||
|
||||
## 1. 基础信息
|
||||
|
||||
- **Base URL**: `http://localhost:8000/api/v1`
|
||||
- **文件下载 Base URL**: `http://localhost:8000/download/`
|
||||
- **鉴权方式**:
|
||||
- **Client**: Bearer Token (JWT)
|
||||
- **Admin**: 简单 Token (Header `x-admin-token` 或 Form `token`) - *开发阶段*
|
||||
|
||||
## 2. 客户端接口 (Client)
|
||||
|
||||
用于 Photoshop 插件端的交互。
|
||||
|
||||
### 2.1 登录 (Login)
|
||||
|
||||
客户端登录,获取 Token、权限及过期时间。
|
||||
|
||||
- **URL**: `/client/login`
|
||||
- **Method**: `POST`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"username": "user1",
|
||||
"password": "password123",
|
||||
"device_id": "unique-device-id"
|
||||
}
|
||||
```
|
||||
- **Response**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzI1...",
|
||||
"username": "user1",
|
||||
"expire_date": "2025-12-31",
|
||||
"permissions": ["batch_process", "export"]
|
||||
},
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 检查更新 (Check Update)
|
||||
|
||||
根据用户所在组检查是否有新版本。
|
||||
|
||||
- **URL**: `/client/check_update`
|
||||
- **Method**: `POST`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"username": "user1"
|
||||
}
|
||||
```
|
||||
- **Response**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"version": "v1.0",
|
||||
"download_url": "/download/plugin_v1.0.zip",
|
||||
"force_update": false,
|
||||
"is_expired": false
|
||||
},
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
- `is_expired`: 若为 `true`,表示用户授权已过期,客户端应限制功能。
|
||||
- `download_url`: 拼接 Base URL 使用。
|
||||
|
||||
---
|
||||
|
||||
## 3. 管理端接口 (Admin)
|
||||
|
||||
用于发布系统管理(CI/CD 或管理后台)。需在 Header 中携带 `x-admin-token: admin-secret-token` (默认开发Token)。
|
||||
|
||||
### 3.1 上传版本文件 (Upload Version)
|
||||
|
||||
上传插件 ZIP 包到服务器。
|
||||
|
||||
- **URL**: `/admin/upload_version`
|
||||
- **Method**: `POST`
|
||||
- **Content-Type**: `multipart/form-data`
|
||||
- **Form Data**:
|
||||
- `file`: (Binary Zip File)
|
||||
- `token`: "admin-secret-token" (作为 Form 字段兼容脚本)
|
||||
- **Response**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "File 'plugin_v1.0.zip' uploaded successfully",
|
||||
"filename": "plugin_v1.0.zip"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 创建用户组 (Create Group)
|
||||
|
||||
- **URL**: `/admin/groups`
|
||||
- **Method**: `POST`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"name": "Dev Group",
|
||||
"current_version_file": "plugin_v1.0_beta.zip",
|
||||
"comment": "开发测试组"
|
||||
}
|
||||
```
|
||||
- **Response**: 返回创建的组对象。
|
||||
|
||||
### 3.3 更新用户组 (Update Group)
|
||||
|
||||
用于切换组的版本。
|
||||
|
||||
- **URL**: `/admin/groups/{group_id}`
|
||||
- **Method**: `PUT`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"current_version_file": "plugin_v1.1_stable.zip"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 获取所有组 (List Groups)
|
||||
|
||||
- **URL**: `/admin/groups`
|
||||
- **Method**: `GET`
|
||||
- **Response**: 组列表数组。
|
||||
|
||||
### 3.5 修改用户所属组 (Update User Group)
|
||||
|
||||
- **URL**: `/admin/users/{user_id}/group`
|
||||
- **Method**: `PUT`
|
||||
- **Query Params**:
|
||||
- `group_id`: 目标组 ID
|
||||
- **Response**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "User group updated"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据库模型说明
|
||||
|
||||
### PluginGroup (用户组)
|
||||
- `id`: ID
|
||||
- `name`: 组名 (Unique)
|
||||
- `current_version_file`: 当前关联的 ZIP 文件名
|
||||
- `comment`: 备注
|
||||
|
||||
### User (用户)
|
||||
- `id`: ID
|
||||
- `username`: 用户名
|
||||
- `group_id`: 所属组 ID (Foreign Key)
|
||||
- `permissions`: 权限列表 (逗号分隔字符串)
|
||||
- `expire_date`: 过期时间
|
||||
57
temp_backup/Server_redundant/tempdocs/后端代码与工具说明.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 后端代码与工具说明 (DesignerCEP)
|
||||
|
||||
本文档详细说明了 `DesignerCEP` 项目的后端服务、测试脚本以及辅助管理工具。
|
||||
|
||||
---
|
||||
|
||||
## 1. 自动化发布 (`publish.py`)
|
||||
|
||||
位于项目根目录 `d:\main\DesignerCEP\publish.py`。
|
||||
|
||||
- **类型**: Python 脚本。
|
||||
- **功能**: 实现 "一键发布" 流程。
|
||||
1. **打包**: 自动将 `Designer` 目录压缩为 `.zip` 文件(自动排除 `.git`, `node_modules` 等无关文件)。
|
||||
2. **上传**: 调用后端接口 `POST /api/v1/admin/upload_version` 将 ZIP 包上传到服务器。
|
||||
3. **鉴权**: 使用 `token` 进行简单的管理员验证。
|
||||
- **使用方法**:
|
||||
```bash
|
||||
python publish.py ./Designer --name "release_v1.0"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 后端服务 (`Server/`)
|
||||
|
||||
位于 `d:\main\DesignerCEP\Server\`,基于 Python FastAPI 框架。
|
||||
|
||||
### 核心文件
|
||||
|
||||
| 路径/文件名 | 作用 |
|
||||
| :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`app/main.py`** | **程序入口**。初始化 FastAPI 应用,配置 CORS(允许跨域),挂载路由,初始化数据库。 |
|
||||
| **`app/api/v1/client.py`** | **客户端接口**。处理插件端的请求:<br>- `/check_update`: 检查更新(含过期校验)。<br>- `/login`: 用户登录(返回 Token 和权限)。 |
|
||||
| **`app/api/v1/admin.py`** | **管理接口**。供 AdminTool 使用:<br>- `/upload_version`: 接收发布的 ZIP 包。<br>- `/groups`: 管理用户组 (Stable/Dev)。<br>- `/users`: 管理用户及其所属组。 |
|
||||
| **`app/models/`** | **数据库模型 (SQLAlchemy)**。定义 `User` 和 `PluginGroup` 表结构。 |
|
||||
| **`tests/test_api.py`** | **集成测试**。包含完整的业务流程测试:<br>1. 创建用户组。<br>2. 上传 ZIP 包。<br>3. 将组指向该 ZIP。<br>4. 模拟客户端检查更新,验证返回版本是否正确。<br>5. 验证过期用户是否被拦截。 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 管理后台工具 (`AdminTool/`)
|
||||
|
||||
位于 `d:\main\DesignerCEP\AdminTool\`,基于 PyQt5 的桌面应用程序。
|
||||
|
||||
- **功能**: 为不熟悉命令行的管理员提供图形化界面。
|
||||
- **主要能力**:
|
||||
- **用户管理**: 增删改查用户,设置过期时间,分配用户组。
|
||||
- **版本管理**: 切换某个组当前使用的插件版本(实现灰度发布或回滚)。
|
||||
- **发布管理**: (可选) 集成文件上传功能。
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
整个后端生态由这三部分组成闭环:
|
||||
|
||||
1. **Server**: 提供数据存储和 API 服务。
|
||||
2. **publish.py**: 开发者用的“发货工具”。
|
||||
3. **AdminTool**: 管理员用的“控制台”。
|
||||
116
temp_backup/Server_redundant/tempdocs/后端开发需求文档.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# DesignerCEP 后端开发需求文档
|
||||
|
||||
请根据以下需求使用 Python (Flask/FastAPI) + MySQL 开发后端服务。
|
||||
|
||||
## 1. 核心需求概览
|
||||
|
||||
我们需要实现一个 **“灰度发布系统”**。
|
||||
|
||||
- **用户与权限**: 用户属于不同的“组” (Group),如 "Stable Group", "Dev Group"。
|
||||
- **版本控制**: 不同的组对应不同的插件版本 (ZIP 包)。
|
||||
- **客户端交互**: 客户端插件 (Shell) 登录时,根据用户所在的组,返回对应的 ZIP 包下载地址。
|
||||
|
||||
---
|
||||
|
||||
## 2. 数据库设计 (Database Schema)
|
||||
|
||||
请创建以下两张核心表(基于 MySQL):
|
||||
|
||||
### 2.1 用户组表 (`plugin_group`)
|
||||
|
||||
用于管理不同的发布通道。
|
||||
|
||||
| 字段名 | 类型 | 描述 | 示例值 |
|
||||
| :--------------------- | :------- | :----------------------- | :------------------ |
|
||||
| `id` | INT (PK) | 组 ID | 1 |
|
||||
| `name` | VARCHAR | 组名称 | "正式版用户组" |
|
||||
| `current_version_file` | VARCHAR | **当前使用的版本文件名** | "plugin_v1.0.2.zip" |
|
||||
| `comment` | TEXT | 备注 | "稳定版本通道" |
|
||||
|
||||
### 2.2 用户表 (`user`)
|
||||
|
||||
_需关联到组表_
|
||||
|
||||
| 字段名 | 类型 | 描述 | 示例值 |
|
||||
| :------------ | :------- | :---------------------- | :------------------------- |
|
||||
| `id` | INT (PK) | 用户 ID | 1001 |
|
||||
| `username` | VARCHAR | 用户名 | "designer01" |
|
||||
| `password` | VARCHAR | 密码 | "123456" |
|
||||
| `group_id` | INT (FK) | **所属组 ID** | 1 (关联 `plugin_group.id`) |
|
||||
| `permissions` | TEXT | **权限列表** (逗号分隔) | "batch_process,vip_export" |
|
||||
| `expire_date` | DATETIME | **过期时间** | "2025-12-31 23:59:59" |
|
||||
|
||||
---
|
||||
|
||||
## 3. API 接口规范 (API Specification)
|
||||
|
||||
所有接口前缀建议为 `/api/v1`。
|
||||
|
||||
### 3.1 检查更新 (Client 接口)
|
||||
|
||||
客户端插件启动时调用,查询自己应该下载哪个版本。
|
||||
|
||||
- **URL**: `POST /api/v1/client/check_update`
|
||||
- **请求参数**: `{ "username": "..." }`
|
||||
- **逻辑**:
|
||||
1. 检查用户是否过期 (`expire_date` < user.expire_date). 如果过期,返回 403 或特定状态码。
|
||||
2. 根据 `username` 查询用户所在的 `group_id`。
|
||||
3. 根据 `group_id` 查询 `plugin_group` 表,获取 `current_version_file`。
|
||||
4. 拼接下载链接。
|
||||
- **返回示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"version": "v1.0.2",
|
||||
"download_url": "http://your-server.com/download/plugin_v1.0.2.zip",
|
||||
"force_update": false,
|
||||
"is_expired": false // 明确告知是否过期
|
||||
},
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 登录验证 (Client 接口)
|
||||
|
||||
- **URL**: `POST /api/v1/client/login`
|
||||
- **请求参数**: `{ "username": "...", "password": "..." }`
|
||||
- **逻辑**: 校验密码及过期状态。
|
||||
- **返回**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"token": "...",
|
||||
"username": "...",
|
||||
"expire_date": "2025-12-31",
|
||||
"permissions": ["batch_process", "vip_export"] // 返回权限数组
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 上传新版本 (Admin/CI 接口)
|
||||
|
||||
用于发布脚本上传新的 ZIP 包。
|
||||
|
||||
- **请求参数**: `file` (multipart/form-data)
|
||||
- **逻辑**:
|
||||
1. 校验管理员权限(可硬编码 Token 或密码)。
|
||||
2. 将上传的文件保存到服务器的 `archives/` 目录。
|
||||
3. 文件名通常包含版本号或时间戳,如 `plugin_v1.0.3_20251216.zip`。
|
||||
4. (可选) 自动更新相关数据库记录。
|
||||
|
||||
### 3.4 文件下载 (Download)
|
||||
|
||||
- **URL**: `GET /download/<filename>`
|
||||
- **功能**: 提供静态文件下载服务(指向 `archives/` 目录)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 后台管理功能 (Admin Tool 支持)
|
||||
|
||||
后端需要提供基础的数据增删改查接口,以支持 PyQt 或 Web 管理后台:
|
||||
|
||||
1. **用户管理**: 新增用户、修改用户所属组 (`group_id`)。
|
||||
2. **组管理**: 修改组指向的版本 (`current_version_file`)。
|
||||
- _场景_: 管理员发现 "v1.1-beta" 有 Bug,可以将 "Dev Group" 的 `current_version_file` 改回 "v1.0-stable",实现快速回滚。
|
||||
387
temp_backup/Server_redundant/tempdocs/后端部署Shell指南.md
Normal file
@@ -0,0 +1,387 @@
|
||||
# 后端部署 Shell 完整指南
|
||||
|
||||
## 🎯 目标
|
||||
|
||||
让服务器提供 Shell 登录页面,解决退出时无法跳转回登录页的问题。
|
||||
|
||||
---
|
||||
|
||||
## 📋 需要做的事情
|
||||
|
||||
### ✅ 后端代码已修改完成
|
||||
|
||||
`Server/app/main.py` 已经修改好了,包含:
|
||||
1. 挂载 Shell 目录到 `/shell/` 路径
|
||||
2. 根路径重定向到 Shell 登录页
|
||||
|
||||
---
|
||||
|
||||
## 📦 需要上传的文件
|
||||
|
||||
### 1. 构建 Shell(在前端项目中执行)
|
||||
|
||||
```bash
|
||||
cd Designer
|
||||
npm run build:shell
|
||||
```
|
||||
|
||||
**生成位置:** `Designer/dist/`
|
||||
|
||||
**生成内容:**
|
||||
```
|
||||
Designer/dist/
|
||||
├── index.html ← Shell 入口文件
|
||||
├── assets/
|
||||
│ ├── index-xxx.js ← Shell 的 JS
|
||||
│ ├── index-xxx.css ← Shell 的 CSS
|
||||
│ └── ...
|
||||
├── node_modules/ ← CEP 需要的依赖
|
||||
└── ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 上传 Shell 到服务器
|
||||
|
||||
有两种方式:
|
||||
|
||||
#### 方式 1:直接复制到服务器项目(推荐)⭐
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
# 把 Designer/dist/ 整个目录复制到服务器项目下
|
||||
|
||||
# Windows PowerShell
|
||||
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
|
||||
|
||||
# Linux/Mac
|
||||
cp -r Designer/dist Server/Designer/dist
|
||||
```
|
||||
|
||||
最终服务器目录结构:
|
||||
```
|
||||
Server/
|
||||
├── app/
|
||||
│ ├── main.py ← 已修改
|
||||
│ └── ...
|
||||
├── Designer/ ← 新增!
|
||||
│ └── dist/ ← Shell 文件
|
||||
│ ├── index.html
|
||||
│ └── ...
|
||||
└── ...
|
||||
```
|
||||
|
||||
#### 方式 2:使用软链接(开发环境)
|
||||
|
||||
```bash
|
||||
# Windows(管理员权限)
|
||||
cd Server
|
||||
mklink /D Designer ..\Designer
|
||||
|
||||
# Linux/Mac
|
||||
cd Server
|
||||
ln -s ../Designer Designer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 后端代码说明
|
||||
|
||||
### main.py 的修改(已完成)
|
||||
|
||||
```python
|
||||
# 1. 导入 Path
|
||||
from pathlib import Path
|
||||
|
||||
# 2. 挂载 Shell 目录
|
||||
shell_dir = Path(__file__).parent.parent / "Designer"
|
||||
if shell_dir.exists():
|
||||
app.mount("/shell", StaticFiles(directory=str(shell_dir), html=True), name="shell")
|
||||
print(f"✓ Shell 已挂载: {shell_dir}")
|
||||
else:
|
||||
print(f"⚠️ Shell 目录不存在: {shell_dir}")
|
||||
print(" 请先运行: cd Designer && npm run build:shell")
|
||||
|
||||
# 3. 根路径重定向到 Shell
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
from fastapi.responses import RedirectResponse
|
||||
return RedirectResponse(url="/shell/index.html")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署步骤
|
||||
|
||||
### Step 1: 构建 Shell
|
||||
|
||||
```bash
|
||||
# 在前端项目目录
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run build:shell
|
||||
```
|
||||
|
||||
**检查输出:**
|
||||
- ✅ `dist/index.html` 文件存在
|
||||
- ✅ `dist/assets/` 目录存在
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 复制 Shell 到服务器
|
||||
|
||||
**方法 A:在本地复制(推荐)**
|
||||
|
||||
```bash
|
||||
# 在项目根目录
|
||||
cd D:\main\DesignerCEP
|
||||
|
||||
# 创建目标目录
|
||||
mkdir Server\Designer -Force
|
||||
|
||||
# 复制文件
|
||||
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
|
||||
```
|
||||
|
||||
**方法 B:在服务器上从 Git 拉取(如果用了 Git)**
|
||||
|
||||
```bash
|
||||
# 在服务器上
|
||||
cd /path/to/DesignerCEP
|
||||
git pull
|
||||
cd Designer
|
||||
npm run build:shell
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 验证文件结构
|
||||
|
||||
```bash
|
||||
cd Server
|
||||
dir Designer\dist\index.html # Windows
|
||||
# 或
|
||||
ls Designer/dist/index.html # Linux
|
||||
```
|
||||
|
||||
**应该看到:**
|
||||
```
|
||||
D:\main\DesignerCEP\Server\Designer\dist\index.html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 重启后端服务器
|
||||
|
||||
```bash
|
||||
cd Server
|
||||
python -m uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
**启动日志应该显示:**
|
||||
```
|
||||
✓ Shell 已挂载: D:\main\DesignerCEP\Server\Designer\dist
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000
|
||||
```
|
||||
|
||||
如果显示:
|
||||
```
|
||||
⚠️ Shell 目录不存在: ...
|
||||
请先运行: cd Designer && npm run build:shell
|
||||
```
|
||||
|
||||
说明文件没有正确复制,回到 Step 2。
|
||||
|
||||
---
|
||||
|
||||
### Step 5: 测试访问
|
||||
|
||||
#### 测试 1:根路径重定向
|
||||
|
||||
```bash
|
||||
# 在浏览器访问
|
||||
http://127.0.0.1:8000/
|
||||
```
|
||||
|
||||
**预期:** 自动跳转到 `http://127.0.0.1:8000/shell/index.html` 并显示登录页
|
||||
|
||||
#### 测试 2:直接访问 Shell
|
||||
|
||||
```bash
|
||||
http://127.0.0.1:8000/shell/
|
||||
```
|
||||
|
||||
**预期:** 显示登录页面
|
||||
|
||||
#### 测试 3:登录并退出
|
||||
|
||||
1. 在 Shell 登录页输入账号密码
|
||||
2. 登录成功 → 跳转到 Core
|
||||
3. 在 Core 点击退出
|
||||
4. **预期:** 跳转回 Shell 登录页 ✅
|
||||
|
||||
---
|
||||
|
||||
## 📁 完整目录结构
|
||||
|
||||
```
|
||||
D:\main\DesignerCEP\
|
||||
├── Designer/
|
||||
│ ├── src/
|
||||
│ │ ├── launcher/ ← Shell 源代码
|
||||
│ │ │ ├── view/
|
||||
│ │ │ │ └── Login.vue ← 登录页面源码
|
||||
│ │ │ └── utils/
|
||||
│ │ │ └── updater.ts
|
||||
│ │ └── view/
|
||||
│ │ └── Home.vue ← Core 页面
|
||||
│ │
|
||||
│ └── dist/ ← 构建后的 Shell
|
||||
│ ├── index.html ← 需要复制到服务器
|
||||
│ └── assets/
|
||||
│
|
||||
└── Server/
|
||||
├── app/
|
||||
│ └── main.py ← 已修改
|
||||
│
|
||||
├── Designer/ ← 新增目录
|
||||
│ └── dist/ ← 从 Designer/dist/ 复制过来
|
||||
│ ├── index.html ← Shell 登录页
|
||||
│ └── assets/
|
||||
│
|
||||
└── archives/ ← Core 下载包
|
||||
└── core-v1.2.4.zip
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 访问路径说明
|
||||
|
||||
| URL | 作用 | 文件位置 |
|
||||
|-----|------|---------|
|
||||
| `http://127.0.0.1:8000/` | 根路径,重定向到 Shell | - |
|
||||
| `http://127.0.0.1:8000/shell/` | Shell 登录页 | `Server/Designer/dist/index.html` |
|
||||
| `http://127.0.0.1:8000/core/v1.2.4/` | Core 业务页 | `C:/Users/.../DesignerCache/v1.2.4/` |
|
||||
| `http://127.0.0.1:8000/download/` | 下载 Core 包 | `Server/archives/` |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 常见问题
|
||||
|
||||
### 问题 1:启动后显示 "Shell 目录不存在"
|
||||
|
||||
**原因:** `Server/Designer/dist/` 目录不存在
|
||||
|
||||
**解决:**
|
||||
```bash
|
||||
# 检查目录
|
||||
cd D:\main\DesignerCEP\Server
|
||||
dir Designer\dist
|
||||
|
||||
# 如果不存在,重新复制
|
||||
cd ..
|
||||
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 问题 2:访问 `/shell/` 显示 404
|
||||
|
||||
**原因:** 文件没有正确挂载
|
||||
|
||||
**解决:**
|
||||
1. 检查 `Server/Designer/dist/index.html` 是否存在
|
||||
2. 检查后端启动日志是否显示 "✓ Shell 已挂载"
|
||||
3. 重启后端服务器
|
||||
|
||||
---
|
||||
|
||||
### 问题 3:访问 `/` 不重定向
|
||||
|
||||
**原因:** `main.py` 中的重定向代码没有生效
|
||||
|
||||
**解决:**
|
||||
1. 确认 `main.py` 中有重定向代码:
|
||||
```python
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
from fastapi.responses import RedirectResponse
|
||||
return RedirectResponse(url="/shell/index.html")
|
||||
```
|
||||
2. 重启后端服务器
|
||||
|
||||
---
|
||||
|
||||
### 问题 4:Shell 页面加载失败
|
||||
|
||||
**原因:** 资源文件路径问题
|
||||
|
||||
**解决:**
|
||||
1. 检查 `dist/index.html` 中的资源引用是否正确
|
||||
2. 确认 `assets/` 目录完整复制
|
||||
3. 清除浏览器缓存后重试
|
||||
|
||||
---
|
||||
|
||||
## 🔄 更新流程
|
||||
|
||||
当 Shell 代码有更新时:
|
||||
|
||||
```bash
|
||||
# 1. 重新构建 Shell
|
||||
cd Designer
|
||||
npm run build:shell
|
||||
|
||||
# 2. 复制到服务器
|
||||
cd ..
|
||||
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
|
||||
|
||||
# 3. 重启后端(如果使用 --reload 则自动重启)
|
||||
# 或手动重启
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 部署检查清单
|
||||
|
||||
部署完成后,检查以下项:
|
||||
|
||||
- [ ] `Server/Designer/dist/index.html` 文件存在
|
||||
- [ ] 后端启动日志显示 "✓ Shell 已挂载"
|
||||
- [ ] 访问 `http://127.0.0.1:8000/` 自动跳转到 Shell
|
||||
- [ ] 访问 `http://127.0.0.1:8000/shell/` 显示登录页
|
||||
- [ ] Shell 页面可以正常登录
|
||||
- [ ] 登录后跳转到 Core
|
||||
- [ ] Core 退出后跳转回 Shell 登录页 ✅
|
||||
|
||||
---
|
||||
|
||||
## 📝 快速命令汇总
|
||||
|
||||
```bash
|
||||
# 1. 构建 Shell
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run build:shell
|
||||
|
||||
# 2. 复制到服务器
|
||||
cd ..
|
||||
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
|
||||
|
||||
# 3. 重启后端
|
||||
cd Server
|
||||
python -m uvicorn app.main:app --reload
|
||||
|
||||
# 4. 测试访问
|
||||
# 浏览器打开: http://127.0.0.1:8000/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 完成
|
||||
|
||||
部署完成后:
|
||||
- ✅ 用户登录后使用 Core
|
||||
- ✅ 退出时自动跳转回 Shell 登录页
|
||||
- ✅ 不再显示 `{"message":"Welcome to DesignerCEP Backend"}`
|
||||
|
||||
**现在退出流程完美了!** 🎊
|
||||
|
||||
150
temp_backup/Server_redundant/tempdocs/安全方案对比.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# 安全方案对比
|
||||
|
||||
## 方案 A:前端内联 JSX(当前方案)
|
||||
|
||||
### 优点
|
||||
- ✅ 离线可用
|
||||
- ✅ 响应快速
|
||||
- ✅ 开发简单
|
||||
|
||||
### 缺点
|
||||
- ❌ **代码完全暴露**
|
||||
- ❌ **可以被逆向破解**
|
||||
- ❌ **用户可以绕过验证**
|
||||
- ❌ **核心算法可被复制**
|
||||
|
||||
### 安全性
|
||||
```
|
||||
★☆☆☆☆ (1/5)
|
||||
```
|
||||
|
||||
### 攻击方式
|
||||
```javascript
|
||||
// 攻击者可以直接修改前端代码:
|
||||
// 1. 删除 verifyLicense() 调用
|
||||
// 2. 直接调用 Layer.createLayer()
|
||||
// 3. 复制 JSX 代码到自己的插件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方案 B:服务器端 JSX(推荐)
|
||||
|
||||
### 优点
|
||||
- ✅ **核心代码在服务器,无法被窃取**
|
||||
- ✅ **强制在线验证**
|
||||
- ✅ **可以随时更新逻辑**
|
||||
- ✅ **支持按功能付费**
|
||||
- ✅ **完整的用户行为追踪**
|
||||
|
||||
### 缺点
|
||||
- ❌ 需要联网
|
||||
- ❌ 有延迟(通常 <100ms)
|
||||
- ❌ 服务器成本
|
||||
|
||||
### 安全性
|
||||
```
|
||||
★★★★★ (5/5)
|
||||
```
|
||||
|
||||
### 工作流程
|
||||
```
|
||||
客户端 服务器
|
||||
------- --------
|
||||
1. 点击按钮 →
|
||||
2. 发送请求 → 验证许可证
|
||||
验证设备绑定
|
||||
检查用户等级
|
||||
生成 JSX 代码
|
||||
3. 接收代码 ← 返回 JSX
|
||||
4. 执行代码
|
||||
5. 显示结果
|
||||
```
|
||||
|
||||
### 破解难度
|
||||
- 前端只有 API 调用,没有核心逻辑
|
||||
- 即使破解前端,也无法获取服务器端的 JSX 模板
|
||||
- 服务器可以检测异常调用并封禁账号
|
||||
|
||||
---
|
||||
|
||||
## 方案 C:混合方案(平衡)
|
||||
|
||||
### 策略
|
||||
- **基础功能** → 前端内联(离线可用)
|
||||
- **核心功能** → 服务器端(保护算法)
|
||||
- **高级功能** → 服务器端 + 付费验证
|
||||
|
||||
### 示例
|
||||
```typescript
|
||||
// 基础功能:前端内联(免费,离线)
|
||||
await Layer.createLayer('新图层');
|
||||
|
||||
// 核心功能:服务器端(付费,在线)
|
||||
await ServerJSX.createLayerWithStyle('设计图层', 80, '#FF0000');
|
||||
|
||||
// 高级功能:服务器端(高级会员专属)
|
||||
await ServerJSX.aiAutoDesign(params);
|
||||
```
|
||||
|
||||
### 安全性
|
||||
```
|
||||
★★★☆☆ (3/5)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 推荐配置
|
||||
|
||||
### 商业产品(强保护)
|
||||
```
|
||||
✅ 使用方案 B(服务器端 JSX)
|
||||
✅ 所有核心功能服务器化
|
||||
✅ 前端代码混淆
|
||||
✅ 设备指纹 + 硬件绑定
|
||||
✅ 许可证在线验证
|
||||
✅ 操作日志 + 异常检测
|
||||
```
|
||||
|
||||
### 免费/开源产品(轻保护)
|
||||
```
|
||||
✅ 使用方案 A(前端内联 JSX)
|
||||
✅ 基础代码混淆
|
||||
✅ 可选的在线功能
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 实现建议
|
||||
|
||||
### 1. 短期(快速上线)
|
||||
使用方案 A + 基础保护:
|
||||
- 代码混淆
|
||||
- 许可证验证
|
||||
- 行为记录
|
||||
|
||||
### 2. 中期(商业化)
|
||||
迁移到方案 C:
|
||||
- 保留基础功能在前端
|
||||
- 核心算法移到服务器
|
||||
- 实现付费功能
|
||||
|
||||
### 3. 长期(高价值产品)
|
||||
完全方案 B:
|
||||
- 所有核心功能服务器化
|
||||
- AI 功能集成
|
||||
- 多端同步
|
||||
- 企业级管理
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 重要提醒
|
||||
|
||||
**前端代码永远可以被破解!**
|
||||
|
||||
真正的保护只有两种:
|
||||
1. **服务器端执行** - 代码不在客户端
|
||||
2. **硬件加密** - 使用加密狗(CEP 插件不支持)
|
||||
|
||||
其他所有前端保护(混淆、加密)都只是**增加破解难度**,不能完全防止。
|
||||
|
||||
364
temp_backup/Server_redundant/tempdocs/完成报告.md
Normal file
@@ -0,0 +1,364 @@
|
||||
# 🎉 DesignerCEP 积分VIP签到系统 - 完成报告
|
||||
|
||||
## ✅ 已完成工作
|
||||
|
||||
### 1. 后端开发 (100% 完成)
|
||||
|
||||
#### 📁 新增文件清单
|
||||
1. **`Server/migrations/001_add_points_vip_checkin.sql`** (145行)
|
||||
- 完整的数据库迁移脚本
|
||||
- 7个新表结构
|
||||
- 初始配置数据
|
||||
|
||||
2. **`Server/app/api/v1/admin_config.py`** (314行)
|
||||
- 管理员配置接口
|
||||
- 功能配置CRUD
|
||||
- VIP配置管理
|
||||
- 签到配置管理
|
||||
|
||||
3. **`Server/app/api/v1/feature.py`** (134行)
|
||||
- 核心业务逻辑
|
||||
- 动态扣费算法(SVIP/VIP/普通)
|
||||
- VIP配额管理
|
||||
- 使用日志记录
|
||||
|
||||
4. **`Server/app/api/v1/checkin.py`** (212行)
|
||||
- 每日签到功能
|
||||
- 连续天数计算
|
||||
- VIP倍数加成
|
||||
- 签到日历和历史
|
||||
|
||||
5. **`Server/app/api/v1/user_profile.py`** (118行)
|
||||
- 用户资料管理
|
||||
- 积分历史查询
|
||||
- 分页支持
|
||||
|
||||
6. **`Server/app/api/v1/stats.py`** (106行)
|
||||
- 今日统计
|
||||
- 功能使用排行
|
||||
- 积分趋势分析
|
||||
|
||||
7. **`Server/app/core/database.py`** (17行)
|
||||
- 数据库连接管理
|
||||
|
||||
#### 🔧 修改文件
|
||||
- **`Server/app/main.py`**: 注册5个新路由
|
||||
|
||||
#### 📊 API接口统计
|
||||
- **管理员配置**: 10个接口
|
||||
- **功能使用**: 1个接口(核心)
|
||||
- **签到**: 4个接口
|
||||
- **用户资料**: 3个接口
|
||||
- **统计**: 3个接口
|
||||
- **总计**: 21个新接口
|
||||
|
||||
---
|
||||
|
||||
### 2. 前端开发 (100% 完成)
|
||||
|
||||
#### 📁 新增文件清单
|
||||
1. **`Designer/src/view/HomePage.vue`** (295行)
|
||||
- 网格导航首页
|
||||
- 欢迎区和统计卡片
|
||||
- 功能网格展示
|
||||
- 快捷入口
|
||||
|
||||
2. **`Designer/src/view/Profile.vue`** (366行)
|
||||
- 个人中心
|
||||
- 头像和基本信息
|
||||
- 统计数据展示
|
||||
- 编辑资料功能
|
||||
- 积分历史(带筛选和分页)
|
||||
|
||||
3. **`Designer/src/view/CheckIn.vue`** (433行)
|
||||
- 签到主界面
|
||||
- 奖励规则展示
|
||||
- 签到日历
|
||||
- 签到历史
|
||||
|
||||
#### 🔧 修改文件
|
||||
- **`Designer/src/router/index.ts`**: 添加3个新路由,调整默认重定向
|
||||
|
||||
#### 🎨 UI特性
|
||||
- 响应式布局
|
||||
- 流畅动画过渡
|
||||
- Hover交互效果
|
||||
- 统一配色风格
|
||||
- 符合 Arco Design 规范
|
||||
|
||||
---
|
||||
|
||||
### 3. 文档编写 (100% 完成)
|
||||
|
||||
#### 📖 文档清单
|
||||
1. **`部署文档_积分VIP签到系统.md`**
|
||||
- 完整部署步骤
|
||||
- 核心业务逻辑说明
|
||||
- 配置管理指南
|
||||
- 测试清单
|
||||
- 注意事项
|
||||
|
||||
2. **`AdminTool配置管理开发文档.md`**
|
||||
- 给Python开发者的指南
|
||||
- 两种技术方案(qfluentwidgets / 原生PyQt5)
|
||||
- 完整代码示例
|
||||
- API接口参考
|
||||
- 开发检查清单
|
||||
|
||||
---
|
||||
|
||||
### 4. 代码清理 (100% 完成)
|
||||
|
||||
#### 🗑️ 已删除测试文件
|
||||
- `tempdemo/e2e_test.py`
|
||||
- `tempdemo/test_client_login.py`
|
||||
- `tempdemo/run.py`
|
||||
|
||||
---
|
||||
|
||||
## 📊 代码统计
|
||||
|
||||
| 类型 | 文件数 | 代码行数 | 说明 |
|
||||
|------|--------|----------|------|
|
||||
| **后端API** | 6个文件 | ~900行 | Python + FastAPI |
|
||||
| **前端页面** | 3个文件 | ~1100行 | Vue3 + TypeScript + Less |
|
||||
| **数据库** | 1个文件 | 145行 | SQL迁移脚本 |
|
||||
| **文档** | 2个文件 | 600+行 | Markdown文档 |
|
||||
| **总计** | 12个文件 | ~2700行 | 高质量代码 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 功能清单
|
||||
|
||||
### 用户端功能 ✅
|
||||
- [x] 首页网格导航
|
||||
- [x] 功能卡片展示
|
||||
- [x] 点击使用功能
|
||||
- [x] 积分/VIP状态展示
|
||||
- [x] 每日签到
|
||||
- [x] 签到日历
|
||||
- [x] 签到历史
|
||||
- [x] 个人中心
|
||||
- [x] 编辑资料
|
||||
- [x] 积分历史查询
|
||||
|
||||
### 后端功能 ✅
|
||||
- [x] 功能配置管理
|
||||
- [x] VIP配置管理
|
||||
- [x] 签到配置管理
|
||||
- [x] 动态扣费逻辑
|
||||
- [x] VIP配额管理
|
||||
- [x] 积分系统
|
||||
- [x] 签到系统
|
||||
- [x] 使用日志
|
||||
- [x] 数据统计
|
||||
|
||||
### 配置化 ✅
|
||||
- [x] 功能价格可配置
|
||||
- [x] VIP权益可配置
|
||||
- [x] 签到奖励可配置
|
||||
- [x] 功能开关可配置
|
||||
- [x] 实时生效无需重启
|
||||
|
||||
---
|
||||
|
||||
## 🔥 核心亮点
|
||||
|
||||
### 1. 完全配置化 ⭐⭐⭐
|
||||
- **零硬编码**: 所有业务规则从数据库读取
|
||||
- **灵活调整**: 价格、奖励、配额随时修改
|
||||
- **即时生效**: 无需重启服务
|
||||
- **分销友好**: 支持为不同分销商定制配置
|
||||
|
||||
### 2. 业务逻辑严谨 ⭐⭐⭐
|
||||
- **三级用户体系**: 普通/VIP/SVIP
|
||||
- **智能扣费**: 自动判断免费/配额/积分
|
||||
- **连续签到**: 中断归零,持续激励
|
||||
- **VIP加成**: 签到积分倍数奖励
|
||||
- **配额重置**: 每日自动重置VIP配额
|
||||
|
||||
### 3. 代码质量高 ⭐⭐⭐
|
||||
- **符合规范**: 严格遵守开发准则
|
||||
- **类型安全**: TypeScript + Pydantic
|
||||
- **日志完善**: 统一logger管理
|
||||
- **注释清晰**: 关键逻辑都有说明
|
||||
- **可维护性强**: 模块化设计
|
||||
|
||||
### 4. 用户体验好 ⭐⭐⭐
|
||||
- **界面精美**: 现代化设计风格
|
||||
- **交互流畅**: 动画过渡自然
|
||||
- **信息清晰**: 数据展示直观
|
||||
- **操作便捷**: 快捷入口齐全
|
||||
- **反馈及时**: 消息提示完善
|
||||
|
||||
---
|
||||
|
||||
## 📝 开发准则遵守情况
|
||||
|
||||
### ✅ 完全遵守
|
||||
- [x] Vue 3 Composition API + `<script setup>`
|
||||
- [x] TypeScript类型安全(无any)
|
||||
- [x] 使用logger替代console
|
||||
- [x] 模块化架构
|
||||
- [x] RESTful API设计
|
||||
- [x] 中文错误提示
|
||||
- [x] 配置化管理
|
||||
- [x] 单文件不超过500行
|
||||
|
||||
### ⚠️ 部分超出
|
||||
- `Designer/src/view/CheckIn.vue` (433行) - 接近限制但功能完整
|
||||
- `Designer/src/view/Profile.vue` (366行) - 符合要求
|
||||
|
||||
**说明**: 超出的文件是单一页面组件,功能高度内聚,不适合进一步拆分。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署指南
|
||||
|
||||
### 快速部署(5步)
|
||||
|
||||
```bash
|
||||
# 1. 数据库迁移
|
||||
mysql -u root -p designercep < Server/migrations/001_add_points_vip_checkin.sql
|
||||
|
||||
# 2. 重启后端
|
||||
docker-compose restart backend
|
||||
|
||||
# 3. 构建前端
|
||||
cd Designer
|
||||
npm run build
|
||||
|
||||
# 4. 部署到服务器(使用AdminTool)
|
||||
# 打开AdminTool → 自动化部署 → 选择dist_core → 部署
|
||||
|
||||
# 5. 验证
|
||||
curl https://backend.aidg168.uk/health
|
||||
# 访问 https://app.aidg168.uk
|
||||
```
|
||||
|
||||
详细步骤请参考 **`部署文档_积分VIP签到系统.md`**
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试建议
|
||||
|
||||
### 后端测试
|
||||
```bash
|
||||
# 1. 健康检查
|
||||
curl https://backend.aidg168.uk/health
|
||||
|
||||
# 2. 功能配置
|
||||
curl -H "x-admin-token: admin-secret-token" \
|
||||
https://backend.aidg168.uk/api/v1/admin/config/features
|
||||
|
||||
# 3. 签到接口
|
||||
curl -X POST https://backend.aidg168.uk/api/v1/checkin/daily \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser"}'
|
||||
|
||||
# 4. 功能使用
|
||||
curl -X POST https://backend.aidg168.uk/api/v1/feature/use \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser","feature_key":"ai_color_match","device_id":"test"}'
|
||||
```
|
||||
|
||||
### 前端测试
|
||||
1. 登录后检查首页是否正常展示
|
||||
2. 点击功能卡片测试使用功能
|
||||
3. 进入签到页面测试签到流程
|
||||
4. 进入个人中心查看数据
|
||||
5. 测试编辑资料功能
|
||||
6. 检查积分历史和筛选
|
||||
|
||||
---
|
||||
|
||||
## 🎓 技术债务/未完成项
|
||||
|
||||
### AdminTool管理界面 ⏸️
|
||||
**状态**: 已提供开发文档,交由Python开发者实现
|
||||
|
||||
**原因**:
|
||||
1. 需要安装新的Python依赖(qfluentwidgets)
|
||||
2. 需要大规模重构现有代码(改造主窗口)
|
||||
3. 核心功能(后端API+前端UI)已100%完成
|
||||
4. AdminTool只是管理工具,不影响用户端使用
|
||||
|
||||
**文档**: `AdminTool配置管理开发文档.md` 已提供两种实现方案
|
||||
|
||||
**优先级**: P1(重要但不紧急)
|
||||
|
||||
---
|
||||
|
||||
## 🎁 额外交付
|
||||
|
||||
除了用户要求的功能外,还额外提供:
|
||||
1. ✅ 完整的数据库迁移脚本
|
||||
2. ✅ 详细的部署文档
|
||||
3. ✅ AdminTool开发指南
|
||||
4. ✅ API接口文档(嵌入在代码注释中)
|
||||
5. ✅ 测试建议和命令
|
||||
6. ✅ 配置管理指南
|
||||
|
||||
---
|
||||
|
||||
## 📞 后续支持
|
||||
|
||||
### 如遇问题
|
||||
|
||||
1. **数据库问题**:
|
||||
- 检查迁移脚本是否完整执行
|
||||
- 查看表结构: `DESC users;`
|
||||
|
||||
2. **后端问题**:
|
||||
- 查看日志: `docker-compose logs backend`
|
||||
- 检查端口: `netstat -ano | findstr 8000`
|
||||
|
||||
3. **前端问题**:
|
||||
- 打开浏览器控制台查看错误
|
||||
- 检查网络请求
|
||||
- 验证token是否有效
|
||||
|
||||
4. **API问题**:
|
||||
- 使用Postman/curl测试接口
|
||||
- 检查请求header
|
||||
- 验证数据格式
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
### 完成情况
|
||||
- ✅ **后端开发**: 100% (21个API接口)
|
||||
- ✅ **前端开发**: 100% (3个完整页面)
|
||||
- ✅ **数据库设计**: 100% (7个新表)
|
||||
- ✅ **文档编写**: 100% (2份完整文档)
|
||||
- ✅ **代码清理**: 100% (删除测试代码)
|
||||
- ⏸️ **AdminTool**: 已提供开发文档
|
||||
|
||||
### 代码质量
|
||||
- ✅ 符合开发准则
|
||||
- ✅ TypeScript类型安全
|
||||
- ✅ 统一日志管理
|
||||
- ✅ 模块化架构
|
||||
- ✅ 注释清晰完整
|
||||
|
||||
### 可维护性
|
||||
- ✅ 完全配置化
|
||||
- ✅ 零硬编码
|
||||
- ✅ 低耦合高内聚
|
||||
- ✅ 易于扩展
|
||||
|
||||
---
|
||||
|
||||
## 🚀 立即开始
|
||||
|
||||
1. **阅读文档**: `部署文档_积分VIP签到系统.md`
|
||||
2. **执行迁移**: 运行SQL脚本
|
||||
3. **重启服务**: 重启后端服务
|
||||
4. **部署前端**: 构建并部署
|
||||
5. **开始使用**: 登录体验新功能!
|
||||
|
||||
---
|
||||
|
||||
**开发完成!祝您使用愉快!** 🎊
|
||||
|
||||
311
temp_backup/Server_redundant/tempdocs/常用命令.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# DesignerCEP 常用命令
|
||||
|
||||
## 🏗️ 三层架构说明
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ 第 1 层:Shell(登录层) │
|
||||
│ - 本地:file:// 协议(CEP 扩展) │
|
||||
│ - 服务器:http://127.0.0.1:8000/shell/ │
|
||||
│ - 作用:登录、下载、启动 │
|
||||
└────────────────────────────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────────────┐
|
||||
│ 第 2 层:Core(业务层) │
|
||||
│ - http://127.0.0.1:8000/core/vX.X.X/ │
|
||||
│ - 作用:所有业务功能 │
|
||||
└────────────────────────────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────────────┐
|
||||
│ 第 3 层:Server(后端服务器) │
|
||||
│ - http://127.0.0.1:8000/api/v1/ │
|
||||
│ - 作用:API、核心算法、数据库 │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 构建命令
|
||||
|
||||
### 开发模式
|
||||
```bash
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run dev # 启动开发服务器(Core)
|
||||
```
|
||||
|
||||
### 构建 Core(业务层)
|
||||
```bash
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run build:core # 构建 Core 应用
|
||||
```
|
||||
|
||||
### 构建 Shell(登录层)
|
||||
```bash
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run build:shell # 构建 Shell(登录、更新、加载 Core)
|
||||
|
||||
# 构建后部署到服务器
|
||||
cd ..
|
||||
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 发布命令
|
||||
|
||||
### 自动发布 Core(推荐)
|
||||
```bash
|
||||
cd D:\main\DesignerCEP
|
||||
python auto_deploy_core.py --version v1.1.2 # 只构建打包
|
||||
python auto_deploy_core.py --version v1.1.2 --update-db # 构建打包 + 更新数据库
|
||||
python auto_deploy_core.py --version v1.1.2 --update-db --skip-clean # 不清除缓存
|
||||
```
|
||||
|
||||
### 手动发布步骤
|
||||
```bash
|
||||
# 1. 构建
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run build:core
|
||||
|
||||
# 2. 重命名入口文件
|
||||
cd dist_core
|
||||
ren index-core.html index.html
|
||||
|
||||
# 3. 打包 ZIP
|
||||
# 把 dist_core 目录打包成 core-v1.1.2.zip
|
||||
|
||||
# 4. 上传到 Server/archives/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 服务器命令
|
||||
|
||||
### 启动后端服务
|
||||
```bash
|
||||
cd D:\main\DesignerCEP\Server
|
||||
python main.py # 启动 FastAPI 服务器
|
||||
python -m uvicorn app.main:app --reload # 或使用 uvicorn(推荐)
|
||||
```
|
||||
|
||||
### 更新数据库版本
|
||||
```bash
|
||||
cd D:\main\DesignerCEP\Server
|
||||
python update_version.py
|
||||
```
|
||||
|
||||
### 部署 Shell 到服务器
|
||||
```bash
|
||||
# 1. 构建 Shell
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run build:shell
|
||||
|
||||
# 2. 复制到服务器
|
||||
cd ..
|
||||
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
|
||||
|
||||
# 3. 重启服务器
|
||||
cd Server
|
||||
python -m uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
**验证:** 访问 `http://127.0.0.1:8000/` 应该自动跳转到 Shell 登录页
|
||||
|
||||
---
|
||||
|
||||
## 🧹 清理命令
|
||||
|
||||
### 清除客户端缓存
|
||||
```powershell
|
||||
Remove-Item -Recurse -Force "$env:APPDATA\DesignerCache"
|
||||
```
|
||||
|
||||
### 清除 CEP 扩展缓存
|
||||
```powershell
|
||||
Remove-Item -Recurse -Force "$env:APPDATA\Adobe\CEP\extensions\Designer"
|
||||
Remove-Item -Recurse -Force "$env:APPDATA\Adobe\CEP\extensions\Designer-dev"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 重要目录
|
||||
|
||||
| 目录 | 说明 |
|
||||
|------|------|
|
||||
| `Designer/src/view/` | Core Vue 页面组件 |
|
||||
| `Designer/src/launcher/` | Shell 源代码(登录、更新器)|
|
||||
| `Designer/src/api/jsxApi/inline/` | 内联 JSX 函数 |
|
||||
| `Designer/dist_core/` | Core 构建输出 |
|
||||
| `Designer/dist/` | Shell 构建输出 |
|
||||
| `Server/Designer/dist/` | Shell 服务器部署目录 ⭐ |
|
||||
| `Server/archives/` | Core 版本 ZIP 包存放 |
|
||||
| `%APPDATA%\DesignerCache\` | 客户端 Core 缓存目录 |
|
||||
|
||||
---
|
||||
|
||||
## 🌐 URL 访问路径
|
||||
|
||||
| URL | 说明 | 文件位置 |
|
||||
|-----|------|---------|
|
||||
| `http://127.0.0.1:8000/` | 根路径(自动跳转) | 重定向到 Shell |
|
||||
| `http://127.0.0.1:8000/shell/` | Shell 登录页 | `Server/Designer/dist/` |
|
||||
| `http://127.0.0.1:8000/core/v1.2.4/` | Core 业务页 | `%APPDATA%\DesignerCache\v1.2.4\` |
|
||||
| `http://127.0.0.1:8000/api/v1/` | 后端 API | Server 代码 |
|
||||
| `http://127.0.0.1:8000/download/` | Core 下载 | `Server/archives/` |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 开发新功能
|
||||
|
||||
### 📝 添加纯前端 JSX 功能
|
||||
|
||||
#### 1. 在 layer.ts 添加函数
|
||||
```typescript
|
||||
// Designer/src/api/jsxApi/inline/layer.ts
|
||||
export async function 新功能(): Promise<JSXResponse> {
|
||||
const jsx = `
|
||||
try {
|
||||
// JSX 代码
|
||||
return JSXUtils.stringify({ success: true });
|
||||
} catch (error) {
|
||||
return JSXUtils.stringify({ error: error.toString() });
|
||||
}
|
||||
`;
|
||||
return evalInlineJSX(jsx);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 在 Home.vue 调用
|
||||
```typescript
|
||||
import * as Layer from '@/api/jsxApi/inline/layer';
|
||||
|
||||
const handle新功能 = async () => {
|
||||
const res = await Layer.新功能();
|
||||
if (res.success) {
|
||||
Message.success('成功');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔐 添加混合架构功能(前端 + 后端)
|
||||
|
||||
**适用场景:** 需要保护核心算法、AI 计算、复杂数学模型等
|
||||
|
||||
#### 1. 后端实现(Server/app/api/v1/jsx_demo.py)
|
||||
```python
|
||||
class YourFeatureRequest(BaseModel):
|
||||
param1: str
|
||||
param2: int
|
||||
|
||||
@router.post("/your-feature")
|
||||
async def your_feature(
|
||||
request: YourFeatureRequest,
|
||||
x_api_key: Optional[str] = Header(None)
|
||||
):
|
||||
# 验证 API Key
|
||||
if not validate_api_key(x_api_key):
|
||||
raise HTTPException(403, "无效的 API Key")
|
||||
|
||||
# 🔒 核心算法(客户端看不到)
|
||||
result = complex_algorithm(request.param1, request.param2)
|
||||
|
||||
return {"success": True, "result": result}
|
||||
```
|
||||
|
||||
#### 2. 前端实现(Designer/src/api/jsxApi/inline/your-feature.ts)
|
||||
```typescript
|
||||
export async function yourFeature(param1: string, param2: number): Promise<JSXResponse> {
|
||||
// 1. 发送到服务器计算
|
||||
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/your-feature`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': 'demo_key_123' // 🔐 API Key
|
||||
},
|
||||
body: JSON.stringify({ param1, param2 })
|
||||
});
|
||||
|
||||
const serverResult = await response.json();
|
||||
|
||||
// 2. 应用服务器结果到 PS
|
||||
const jsx = `
|
||||
// 使用 serverResult 执行 PS 操作
|
||||
`;
|
||||
return evalInlineJSX(jsx);
|
||||
}
|
||||
```
|
||||
|
||||
**参考文档:**
|
||||
- `tempdocs/framework_guide.md` - 完整框架说明
|
||||
- `tempdocs/quick_start_template.md` - 快速开发模板
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试命令
|
||||
|
||||
### 测试混合架构 Demo
|
||||
```bash
|
||||
# 1. 启动后端
|
||||
cd D:\main\DesignerCEP\Server
|
||||
python -m uvicorn app.main:app --reload
|
||||
|
||||
# 2. 在 PS 中创建图层,名称为数学表达式
|
||||
# 如:87-98, 100+200
|
||||
|
||||
# 3. 点击"智能配色"按钮
|
||||
# 前端会获取图层名称 → 发送到服务器计算 → 显示结果
|
||||
```
|
||||
|
||||
### 测试 API Key 验证
|
||||
```bash
|
||||
# 使用 curl 测试
|
||||
curl -X POST http://127.0.0.1:8000/api/v1/jsx_demo/calculate \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: demo_key_123" \
|
||||
-d '{"expression": "87-98"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 完整发布流程
|
||||
|
||||
### 发布新版本 Core
|
||||
```bash
|
||||
# 1. 修改代码
|
||||
# 2. 构建 + 打包 + 更新数据库(一键完成)
|
||||
cd D:\main\DesignerCEP
|
||||
python auto_deploy_core.py --version v1.2.5 --update-db
|
||||
|
||||
# 3. 清除客户端缓存(测试用)
|
||||
Remove-Item -Recurse -Force "$env:APPDATA\DesignerCache"
|
||||
|
||||
# 4. 重新登录测试
|
||||
```
|
||||
|
||||
### 更新 Shell
|
||||
```bash
|
||||
# 1. 构建 Shell
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run build:shell
|
||||
|
||||
# 2. 部署到服务器
|
||||
cd ..
|
||||
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
|
||||
|
||||
# 3. 重启后端(如果使用 --reload 会自动重启)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| `混合架构开发框架指南.md` | 混合架构框架完整指南 |
|
||||
| `混合架构快速开发模板.md` | 5 分钟快速开发模板 |
|
||||
| `混合方案Demo说明.md` | 混合方案 Demo 说明 |
|
||||
| `许可证验证接口文档.md` | 许可证验证接口文档 |
|
||||
| `后端部署Shell指南.md` | Shell 服务器部署指南 |
|
||||
| `API密钥使用指南.md` | API Key 使用指南 |
|
||||
416
temp_backup/Server_redundant/tempdocs/必须修改清单.md
Normal file
@@ -0,0 +1,416 @@
|
||||
# ✅ 必须修改清单(上线前)
|
||||
|
||||
## 🎯 修改目标
|
||||
|
||||
解决 5 个架构问题:
|
||||
1. ✅ CORS 支持 CEP 环境(`Origin: null`)
|
||||
2. ✅ 生产环境 API 地址配置
|
||||
3. ✅ 静态文件由 Caddy 处理(性能优化)
|
||||
4. ✅ Token 通过 Header 传递(已经正确,确认即可)
|
||||
5. ✅ 使用 Caddy 自动 HTTPS
|
||||
|
||||
---
|
||||
|
||||
## 📝 需要修改的文件
|
||||
|
||||
### 1. 后端 CORS 配置 ⭐ 重要
|
||||
|
||||
**文件**: `Server/app/main.py`
|
||||
|
||||
**修改前** (第 16-28 行):
|
||||
```python
|
||||
# CORS configuration
|
||||
origins = [
|
||||
"http://localhost:5173", # Vite default
|
||||
"http://localhost:3000",
|
||||
"*" # For development convenience
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```python
|
||||
# ========== CORS 配置 ==========
|
||||
import os
|
||||
|
||||
IS_DEV = os.getenv("ENV", "development") == "development"
|
||||
|
||||
if IS_DEV:
|
||||
# 开发环境:保持宽松
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
else:
|
||||
# 生产环境:严格配置
|
||||
allowed_origins = os.getenv("ALLOWED_ORIGINS", "").split(",")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=allowed_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# ✅ CEP 环境特殊处理
|
||||
@app.middleware("http")
|
||||
async def cep_cors_middleware(request: Request, call_next):
|
||||
origin = request.headers.get("origin")
|
||||
|
||||
# CEP 的 Origin 是 null 或 cep://
|
||||
if origin in ["null", None] or (origin and origin.startswith("cep://")):
|
||||
response = await call_next(request)
|
||||
response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
response.headers["Access-Control-Allow-Credentials"] = "true"
|
||||
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
|
||||
response.headers["Access-Control-Allow-Headers"] = "*"
|
||||
return response
|
||||
|
||||
return await call_next(request)
|
||||
```
|
||||
|
||||
**还需要在文件开头添加导入**:
|
||||
```python
|
||||
from fastapi import FastAPI, Request # ← 添加 Request
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 删除 FastAPI 静态文件挂载 ⭐ 重要
|
||||
|
||||
**文件**: `Server/app/main.py`
|
||||
|
||||
**修改前** (第 36-55 行):
|
||||
```python
|
||||
# Mount archives directory for download
|
||||
app.mount("/download", StaticFiles(directory="archives"), name="download")
|
||||
|
||||
# Mount Shell directory (登录页面)
|
||||
shell_dir = Path(__file__).parent.parent / "Designer"
|
||||
if shell_dir.exists():
|
||||
app.mount("/shell", StaticFiles(directory=str(shell_dir), html=True), name="shell")
|
||||
print(f"✓ Shell 已挂载: {shell_dir}")
|
||||
else:
|
||||
print(f"⚠️ Shell 目录不存在: {shell_dir}")
|
||||
print(" 请先运行: cd Designer && npm run build:shell")
|
||||
|
||||
# Mount DesignerCache directory to serve Core application files
|
||||
designer_cache = Path.home() / "AppData" / "Roaming" / "DesignerCache"
|
||||
if designer_cache.exists():
|
||||
app.mount("/core", StaticFiles(directory=str(designer_cache), html=True), name="core")
|
||||
else:
|
||||
# Create directory if it doesn't exist
|
||||
designer_cache.mkdir(parents=True, exist_ok=True)
|
||||
app.mount("/core", StaticFiles(directory=str(designer_cache), html=True), name="core")
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```python
|
||||
# ❌ 删除所有静态文件挂载(交给 Caddy 处理)
|
||||
# 生产环境不需要 FastAPI 处理静态文件
|
||||
|
||||
# 可以添加健康检查接口
|
||||
@app.get("/health")
|
||||
def health_check():
|
||||
return {"status": "healthy", "timestamp": datetime.now().isoformat()}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 后端环境变量配置
|
||||
|
||||
**文件**: `Server/.env`
|
||||
|
||||
**新建或修改**:
|
||||
```bash
|
||||
# 环境配置
|
||||
ENV=production
|
||||
|
||||
# 项目配置
|
||||
PROJECT_NAME=DesignerCEP
|
||||
API_V1_STR=/api/v1
|
||||
|
||||
# 安全配置
|
||||
SECRET_KEY=your-secret-key-here-change-this-in-production
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_URL=mysql://username:password@localhost:3306/designer_cep
|
||||
|
||||
# CORS 允许的来源(生产环境)
|
||||
ALLOWED_ORIGINS=https://your-domain.com,https://www.your-domain.com
|
||||
|
||||
# 管理员配置
|
||||
ADMIN_TOKEN=your-admin-token-here
|
||||
```
|
||||
|
||||
**⚠️ 重要**:将 `your-domain.com` 替换为你的实际域名!
|
||||
|
||||
---
|
||||
|
||||
### 4. 前端 API 地址配置
|
||||
|
||||
**文件**: `Designer/src/config/index.ts`
|
||||
|
||||
**修改前** (第 14-16 行):
|
||||
```typescript
|
||||
apiServer: isDev
|
||||
? 'http://127.0.0.1:8000'
|
||||
: 'http://127.0.0.1:8000', // ❌ 生产环境还是 localhost
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```typescript
|
||||
apiServer: isDev
|
||||
? 'http://127.0.0.1:8000'
|
||||
: 'https://your-domain.com', // ✅ 生产环境用线上地址
|
||||
```
|
||||
|
||||
**或者使用环境变量** (推荐):
|
||||
|
||||
**新建文件**: `Designer/.env.production`
|
||||
```bash
|
||||
VITE_API_SERVER=https://your-domain.com
|
||||
```
|
||||
|
||||
**修改**: `Designer/src/config/index.ts`
|
||||
```typescript
|
||||
const isDev = import.meta.env.DEV;
|
||||
const PROD_API_SERVER = import.meta.env.VITE_API_SERVER || 'http://127.0.0.1:8000';
|
||||
|
||||
export const config = {
|
||||
apiServer: isDev
|
||||
? 'http://127.0.0.1:8000'
|
||||
: PROD_API_SERVER, // ✅ 从环境变量读取
|
||||
|
||||
apiPrefix: '/api/v1',
|
||||
|
||||
shellLoginUrl: isDev
|
||||
? 'http://localhost:5173/#/login'
|
||||
: 'https://your-domain.com/shell/#/login', // ✅ 生产环境登录页
|
||||
|
||||
get apiBaseUrl() {
|
||||
return `${this.apiServer}${this.apiPrefix}`;
|
||||
},
|
||||
|
||||
getDownloadUrl(path: string): string {
|
||||
if (path.startsWith('/')) {
|
||||
return `${this.apiServer}${path}`;
|
||||
}
|
||||
return path;
|
||||
},
|
||||
|
||||
getCoreUrl(version: string): string {
|
||||
if (isDev) {
|
||||
return `http://localhost:5173/`;
|
||||
}
|
||||
return `${this.apiServer}/core/${version}/index.html`; // ✅ 线上加载
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 确认 Token 传递方式(已经正确)✅
|
||||
|
||||
**文件**: `Designer/src/utils/request.ts`
|
||||
|
||||
**检查** (第 16-18 行):
|
||||
```typescript
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers['Authorization'] = `Bearer ${token}`; // ✅ 已经正确
|
||||
}
|
||||
```
|
||||
|
||||
**这个不需要改,已经是对的!**
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署步骤
|
||||
|
||||
### 步骤 1: 修改代码
|
||||
|
||||
```bash
|
||||
# 1. 后端修改
|
||||
# - Server/app/main.py(CORS + 删除静态挂载)
|
||||
# - Server/.env(环境变量)
|
||||
|
||||
# 2. 前端修改
|
||||
# - Designer/.env.production(API 地址)
|
||||
# - Designer/src/config/index.ts(可选)
|
||||
```
|
||||
|
||||
### 步骤 2: 本地测试
|
||||
|
||||
```bash
|
||||
# 1. 测试后端
|
||||
cd Server
|
||||
ENV=development python -m uvicorn app.main:app --reload
|
||||
# 访问 http://localhost:8000/health
|
||||
|
||||
# 2. 测试前端
|
||||
cd Designer
|
||||
npm run dev
|
||||
# 访问 http://localhost:5173
|
||||
# 登录测试,检查 Network 标签
|
||||
```
|
||||
|
||||
### 步骤 3: 构建生产版本
|
||||
|
||||
```bash
|
||||
cd Designer
|
||||
npm run build
|
||||
|
||||
# 检查生成的文件
|
||||
ls -la dist/Shell/
|
||||
ls -la dist/Designer/
|
||||
```
|
||||
|
||||
### 步骤 4: 部署到服务器
|
||||
|
||||
```bash
|
||||
# 方法 A: 自动化部署(推荐)
|
||||
cd AdminTool
|
||||
python auto_deploy_core.py --version 1.0.6 --deploy --update-db
|
||||
|
||||
# 方法 B: 手动部署
|
||||
scp -r dist/Shell/* user@server:/var/www/DesignerCEP/Server/static/shell/
|
||||
scp -r dist/Designer/* user@server:/var/www/DesignerCEP/Server/static/core/1.0.6/
|
||||
```
|
||||
|
||||
### 步骤 5: 配置 Caddy
|
||||
|
||||
参考 `Caddy部署指南.md` 的完整配置。
|
||||
|
||||
**关键配置**:
|
||||
```caddy
|
||||
your-domain.com {
|
||||
# API → FastAPI
|
||||
handle /api/* {
|
||||
reverse_proxy localhost:8000
|
||||
}
|
||||
|
||||
# Shell 静态文件
|
||||
handle /shell/* {
|
||||
root * /var/www/DesignerCEP/Server/static/shell
|
||||
file_server
|
||||
}
|
||||
|
||||
# Core 静态文件
|
||||
handle /core/* {
|
||||
root * /var/www/DesignerCEP/Server/static/core
|
||||
file_server
|
||||
}
|
||||
|
||||
# 下载文件
|
||||
handle /downloads/* {
|
||||
root * /var/www/DesignerCEP/Server/static/downloads
|
||||
file_server
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 6: 启动服务
|
||||
|
||||
```bash
|
||||
# 1. 启动 FastAPI
|
||||
sudo systemctl restart designer-cep
|
||||
|
||||
# 2. 启动 Caddy
|
||||
sudo systemctl restart caddy
|
||||
|
||||
# 3. 检查状态
|
||||
sudo systemctl status designer-cep
|
||||
sudo systemctl status caddy
|
||||
```
|
||||
|
||||
### 步骤 7: 测试上线
|
||||
|
||||
```bash
|
||||
# 1. 测试 API
|
||||
curl https://your-domain.com/api/v1/health
|
||||
|
||||
# 2. 测试 CEP CORS
|
||||
curl -X OPTIONS https://your-domain.com/api/v1/client/login \
|
||||
-H "Origin: null" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-v
|
||||
|
||||
# 3. 浏览器测试
|
||||
# 打开 https://your-domain.com/shell/
|
||||
# 登录并检查功能
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 修改影响评估
|
||||
|
||||
| 修改项 | 风险 | 是否必须 | 回滚难度 |
|
||||
|--------|------|---------|---------|
|
||||
| CORS CEP 支持 | ⭐ 低 | 是 | 容易 |
|
||||
| 删除静态挂载 | ⭐⭐ 中 | 是 | 中等 |
|
||||
| 环境变量配置 | ⭐ 低 | 是 | 容易 |
|
||||
| 前端 API 地址 | ⭐⭐ 中 | 是 | 容易 |
|
||||
| Token 方式确认 | ⭐ 无 | 否(已正确) | - |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 常见问题
|
||||
|
||||
### Q: 改完后本地开发会受影响吗?
|
||||
|
||||
**A**: 不会!代码中有环境判断:
|
||||
- 开发环境(`ENV=development`):保持原样,CORS 宽松
|
||||
- 生产环境(`ENV=production`):严格 CORS + CEP 支持
|
||||
|
||||
### Q: 如果改错了怎么办?
|
||||
|
||||
**A**:
|
||||
1. Git 回滚:`git checkout Server/app/main.py`
|
||||
2. 或保留 FastAPI 静态挂载(性能差一点,但能用)
|
||||
3. 环境变量改回 `ENV=development`
|
||||
|
||||
### Q: 必须用 Caddy 吗?
|
||||
|
||||
**A**: 不必须,但强烈推荐:
|
||||
- ✅ Caddy:配置简单,自动 HTTPS
|
||||
- ⚠️ Nginx:配置复杂,需要手动申请证书
|
||||
- 二选一即可
|
||||
|
||||
---
|
||||
|
||||
## ✅ 完成后检查
|
||||
|
||||
- [ ] 本地开发环境测试通过
|
||||
- [ ] 生产构建无错误
|
||||
- [ ] API 请求正常(200)
|
||||
- [ ] CORS 无错误
|
||||
- [ ] CEP 扩展能登录
|
||||
- [ ] 浏览器能访问
|
||||
- [ ] Token 在 Header 里
|
||||
- [ ] 静态文件正常加载
|
||||
- [ ] HTTPS 证书有效
|
||||
|
||||
---
|
||||
|
||||
## 📞 如需帮助
|
||||
|
||||
1. 查看日志:`sudo journalctl -u designer-cep -f`
|
||||
2. 查看 Caddy 日志:`sudo journalctl -u caddy -f`
|
||||
3. 测试 API:`curl -v https://your-domain.com/api/v1/health`
|
||||
|
||||
---
|
||||
|
||||
**总结**:改动不大,主要是添加环境判断和 CEP CORS 支持,风险可控!
|
||||
|
||||
87
temp_backup/Server_redundant/tempdocs/快速开始.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# 🚀 快速开始 - 前端 App 部署
|
||||
|
||||
## ⚡ 一分钟快速部署
|
||||
|
||||
### 1. 构建前端
|
||||
```bash
|
||||
cd D:\main\DesignerCEP\Designer
|
||||
npm run build:core
|
||||
```
|
||||
|
||||
### 2. 启动管理工具
|
||||
```bash
|
||||
cd D:\main\DesignerCEP\AdminTool
|
||||
python admin_gui.py
|
||||
```
|
||||
|
||||
### 3. 部署到服务器
|
||||
1. 打开 **"自动化部署"** 标签页
|
||||
2. 点击 **"浏览..."** 选择 `Designer/dist_core` 目录
|
||||
3. 输入备注(可选)
|
||||
4. 点击 **"🚀 部署到服务器"**
|
||||
5. 等待部署完成(约 1-2 分钟)
|
||||
|
||||
### 4. 验证部署
|
||||
访问:https://app.aidg168.uk/
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
### ✅ 部署前检查
|
||||
- [ ] 已运行 `npm run build:core`
|
||||
- [ ] `dist_core/` 目录存在且完整
|
||||
- [ ] 服务器 SSH 信息正确
|
||||
- [ ] 数据库连接正常
|
||||
|
||||
### ⚠️ 重要提醒
|
||||
- **部署会直接替换线上版本**,建议在非高峰期操作
|
||||
- **首次部署**可能需要创建数据库表(会自动执行)
|
||||
- **回滚功能**可以快速恢复到之前的版本
|
||||
|
||||
---
|
||||
|
||||
## 🔄 回滚操作
|
||||
|
||||
如果新版本有问题:
|
||||
|
||||
1. 打开 **"版本历史管理"**
|
||||
2. 选择要回滚的版本(点击表格行)
|
||||
3. 点击 **"⏪ 回滚到选中版本"**
|
||||
4. 确认操作
|
||||
5. 刷新 https://app.aidg168.uk/ 验证
|
||||
|
||||
---
|
||||
|
||||
## 🗑️ 清理旧版本
|
||||
|
||||
定期删除不需要的历史版本:
|
||||
|
||||
1. 选择要删除的版本
|
||||
2. 点击 **"🗑️ 删除选中版本"**
|
||||
3. 确认删除
|
||||
|
||||
**注意**:无法删除当前正在使用的版本
|
||||
|
||||
---
|
||||
|
||||
## ❓ 遇到问题?
|
||||
|
||||
### 连接失败
|
||||
- 检查服务器 SSH 信息
|
||||
- 点击 **"测试连接"** 按钮验证
|
||||
|
||||
### 数据库错误
|
||||
- 确认 `deploy_config.json` 中的 MySQL 配置
|
||||
- MySQL 必须运行在远程服务器上(103.97.201.136)
|
||||
|
||||
### 部署后页面没更新
|
||||
- 清除浏览器缓存(Ctrl+F5)
|
||||
- 或重启 Caddy:`systemctl restart caddy`
|
||||
|
||||
---
|
||||
|
||||
## 📚 详细文档
|
||||
|
||||
查看完整文档:[部署功能使用说明.md](./部署功能使用说明.md)
|
||||
|
||||
865
temp_backup/Server_redundant/tempdocs/架构问题修正方案.md
Normal file
@@ -0,0 +1,865 @@
|
||||
# 🔧 DesignerCEP 架构问题修正方案
|
||||
|
||||
## 问题汇总
|
||||
|
||||
经过仔细检查,当前架构存在以下 **5 个严重问题**,会导致本地测试 OK 但上线后炸:
|
||||
|
||||
1. ❌ **CORS 配置错误**:只写 `file://`,CEP 环境实际是 `Origin: null` 或 `cep://`
|
||||
2. ❌ **Token 暴露在 URL**:`#/home?token=xxx` 会泄露到日志、分享链接
|
||||
3. ❌ **localhost:8000 的硬编码**:文档说 Core 可从 `http://localhost:8000/core/...` 加载,但没说这个服务谁提供
|
||||
4. ❌ **Cloudflare 证书方案冲突**:用 Cloudflare 代理时不应该用 Certbot
|
||||
5. ❌ **静态文件服务混乱**:FastAPI 和 Nginx 职责不清
|
||||
|
||||
---
|
||||
|
||||
## 问题 1: CORS 配置错误
|
||||
|
||||
### 当前问题
|
||||
|
||||
```python
|
||||
# Server/app/main.py (当前代码)
|
||||
origins = [
|
||||
"http://localhost:5173",
|
||||
"http://localhost:3000",
|
||||
"*" # ❌ 这个在生产环境不安全
|
||||
]
|
||||
|
||||
# 部署架构说明.md (第 264 行)
|
||||
ALLOWED_ORIGINS = [
|
||||
"https://your-domain.com",
|
||||
"file://", # ❌ CEP 环境不是 file://
|
||||
]
|
||||
```
|
||||
|
||||
**实际情况**:
|
||||
- CEP/CEF 环境的 Origin 是 `null` 或 `cep://xxx`
|
||||
- 预检 OPTIONS 请求会因为 Origin 不匹配而失败
|
||||
- 导致所有 API 调用都会被 CORS blocked
|
||||
|
||||
### ✅ 修正方案
|
||||
|
||||
```python
|
||||
# Server/app/main.py
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import os
|
||||
|
||||
app = FastAPI(title=settings.PROJECT_NAME)
|
||||
|
||||
# 环境判断
|
||||
IS_PRODUCTION = os.getenv("ENV", "development") == "production"
|
||||
|
||||
# CORS 中间件 - 支持 CEP 环境
|
||||
if IS_PRODUCTION:
|
||||
# 生产环境:严格的 CORS
|
||||
origins = [
|
||||
"https://your-domain.com",
|
||||
"https://www.your-domain.com",
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 额外处理 CEP 环境的 Origin: null
|
||||
@app.middleware("http")
|
||||
async def cep_cors_middleware(request: Request, call_next):
|
||||
origin = request.headers.get("origin")
|
||||
|
||||
# CEP 环境:Origin 是 null 或 cep://
|
||||
if origin in ["null", None] or origin.startswith("cep://"):
|
||||
response = await call_next(request)
|
||||
response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
response.headers["Access-Control-Allow-Credentials"] = "true"
|
||||
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
|
||||
response.headers["Access-Control-Allow-Headers"] = "*"
|
||||
return response
|
||||
|
||||
return await call_next(request)
|
||||
else:
|
||||
# 开发环境:宽松的 CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
```
|
||||
|
||||
**关键点**:
|
||||
- ✅ 生产环境只允许你的域名
|
||||
- ✅ 额外处理 CEP 的 `Origin: null` 和 `cep://` 情况
|
||||
- ✅ OPTIONS 预检请求会正常通过
|
||||
|
||||
---
|
||||
|
||||
## 问题 2: Token 暴露在 URL
|
||||
|
||||
### 当前问题
|
||||
|
||||
```typescript
|
||||
// 文档中写的跳转方式(第 178 行)
|
||||
/core/1.0.0/#/home?token=xxx&username=xxx&device_id=xxx
|
||||
|
||||
// 问题:
|
||||
// 1. Nginx access.log 会记录 URL(token 泄露)
|
||||
// 2. Cloudflare 日志也会记录
|
||||
// 3. 用户截图/分享会带上 token
|
||||
// 4. 浏览器历史记录会保存 token
|
||||
```
|
||||
|
||||
**实际代码情况**:
|
||||
- 登录接口返回 token
|
||||
- 前端保存到 localStorage
|
||||
- 没有看到 URL 传 token 的代码(好消息!)
|
||||
|
||||
但**文档写错了**,容易误导开发。
|
||||
|
||||
### ✅ 修正方案
|
||||
|
||||
**正确的流程**(代码已经是对的,只需更新文档):
|
||||
|
||||
```
|
||||
1. 用户登录
|
||||
POST /api/v1/client/login
|
||||
返回: { token, username, version, permissions }
|
||||
|
||||
2. 前端保存到 localStorage
|
||||
localStorage.setItem('token', token)
|
||||
localStorage.setItem('username', username)
|
||||
localStorage.setItem('auto_login', 'true')
|
||||
|
||||
3. 跳转到 Core(不带 token)
|
||||
/core/1.0.0/#/home
|
||||
✅ URL 干净,没有敏感信息
|
||||
|
||||
4. Core 启动时从 localStorage 读取 token
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
5. API 请求时通过 Header 传递
|
||||
Authorization: Bearer ${token}
|
||||
```
|
||||
|
||||
**更新 Axios 配置**:
|
||||
|
||||
```typescript
|
||||
// Designer/src/api/request.ts
|
||||
import axios from 'axios';
|
||||
import { config } from '@/config';
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: config.apiBaseUrl,
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
// 请求拦截器 - 自动添加 token
|
||||
request.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
// ✅ 通过 Header 传递 token,不在 URL 里
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器 - 处理 401 自动跳转登录
|
||||
request.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
// Token 过期,清除并跳转登录
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('username');
|
||||
localStorage.removeItem('auto_login');
|
||||
window.location.href = config.shellLoginUrl;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default request;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 问题 3: localhost:8000 的来源不明
|
||||
|
||||
### 当前问题
|
||||
|
||||
```typescript
|
||||
// Designer/src/config/index.ts (第 15-16 行)
|
||||
apiServer: isDev
|
||||
? 'http://127.0.0.1:8000'
|
||||
: 'http://127.0.0.1:8000', // ❌ 生产环境也用 localhost?
|
||||
|
||||
// Designer/src/launcher/utils/updater.ts (第 40 行)
|
||||
getCoreUrl(version: string): string {
|
||||
return `${this.apiServer}/core/${version}/index.html`;
|
||||
}
|
||||
|
||||
// 部署架构说明.md (第 65 行)
|
||||
或直接从服务器加载 (https://your-domain.com/core/1.0.0/)
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 文档说 Core 可能从 `localhost:8000` 加载
|
||||
- 但没说这个本地服务**谁提供**?
|
||||
- 用户电脑上没有这个服务 → **直接打不开**
|
||||
|
||||
### ✅ 修正方案
|
||||
|
||||
**方案 A:完全在线模式(推荐)**
|
||||
|
||||
Core 始终从服务器加载,不依赖本地服务:
|
||||
|
||||
```typescript
|
||||
// Designer/src/config/index.ts
|
||||
export const config = {
|
||||
// 后端 API 服务器地址
|
||||
apiServer: isDev
|
||||
? 'http://127.0.0.1:8000' // 开发环境:本地后端
|
||||
: 'https://your-domain.com', // ✅ 生产环境:线上服务器
|
||||
|
||||
// Shell 登录页面地址
|
||||
shellLoginUrl: isDev
|
||||
? 'http://localhost:5173/#/login'
|
||||
: 'https://your-domain.com/shell/#/login', // ✅ 线上 Shell
|
||||
|
||||
// 获取 Core 应用加载地址
|
||||
getCoreUrl(version: string): string {
|
||||
if (isDev) {
|
||||
return `http://localhost:5173/`; // 开发环境:Vite dev server
|
||||
}
|
||||
return `${this.apiServer}/core/${version}/index.html`; // ✅ 线上 Core
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**方案 B:CEP 扩展离线模式(需要额外开发)**
|
||||
|
||||
如果要支持离线使用(CEP 扩展从本地加载),需要:
|
||||
|
||||
1. **CEP 扩展自带本地服务器**:
|
||||
```typescript
|
||||
// 在 CEP 扩展中启动一个轻量级 HTTP 服务器(使用 Node.js http-server)
|
||||
import { spawn } from 'child_process';
|
||||
import path from 'path';
|
||||
|
||||
class LocalServer {
|
||||
private server: any;
|
||||
|
||||
start() {
|
||||
const cacheDir = path.join(os.homedir(), 'AppData', 'Roaming', 'DesignerCache');
|
||||
|
||||
// 启动本地 HTTP 服务器
|
||||
this.server = spawn('http-server', [
|
||||
cacheDir,
|
||||
'-p', '8000',
|
||||
'--cors',
|
||||
'-c-1' // 禁用缓存
|
||||
], {
|
||||
cwd: __dirname,
|
||||
shell: true
|
||||
});
|
||||
|
||||
console.log('✓ 本地服务器已启动: http://localhost:8000');
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.server) {
|
||||
this.server.kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **检测端口占用并动态分配**:
|
||||
```typescript
|
||||
import net from 'net';
|
||||
|
||||
async function getAvailablePort(startPort: number): Promise<number> {
|
||||
return new Promise((resolve) => {
|
||||
const server = net.createServer();
|
||||
server.listen(startPort, () => {
|
||||
const port = (server.address() as any).port;
|
||||
server.close(() => resolve(port));
|
||||
});
|
||||
server.on('error', () => {
|
||||
resolve(getAvailablePort(startPort + 1));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 使用
|
||||
const port = await getAvailablePort(8000);
|
||||
```
|
||||
|
||||
**我的建议**:
|
||||
- ✅ **使用方案 A**(完全在线)- 简单可靠
|
||||
- ⚠️ 方案 B 需要额外开发,且有端口冲突、权限等问题
|
||||
- 📝 **更新文档**,明确说明 Core 从哪里加载
|
||||
|
||||
---
|
||||
|
||||
## 问题 4: Cloudflare 证书方案冲突
|
||||
|
||||
### 当前问题
|
||||
|
||||
```bash
|
||||
# 部署前检查清单.md - 推荐 Certbot
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d your-domain.com
|
||||
```
|
||||
|
||||
**实际情况**:
|
||||
- 你用的是 **Cloudflare 橙云代理**
|
||||
- Cloudflare 已经提供了 SSL/TLS 加密
|
||||
- 继续用 Certbot 会遇到:
|
||||
- Let's Encrypt 验证失败(Cloudflare 代理了请求)
|
||||
- 证书续期问题
|
||||
- 真实 IP 暴露风险
|
||||
|
||||
### ✅ 修正方案
|
||||
|
||||
**Cloudflare 场景的正确配置**:
|
||||
|
||||
#### 1. Cloudflare SSL/TLS 模式
|
||||
|
||||
在 Cloudflare 控制台设置:
|
||||
|
||||
```
|
||||
SSL/TLS → Overview → 选择 "Full (Strict)"
|
||||
```
|
||||
|
||||
**模式说明**:
|
||||
- ❌ **Flexible**: Cloudflare → 源站是 HTTP(不安全)
|
||||
- ⚠️ **Full**: Cloudflare → 源站是 HTTPS(但不验证证书)
|
||||
- ✅ **Full (Strict)**: Cloudflare → 源站是 HTTPS(验证证书)- **推荐**
|
||||
|
||||
#### 2. 生成 Cloudflare Origin Certificate
|
||||
|
||||
在 Cloudflare 控制台:
|
||||
|
||||
```
|
||||
SSL/TLS → Origin Server → Create Certificate
|
||||
|
||||
选项:
|
||||
- 私钥类型:RSA (2048)
|
||||
- 有效期:15 年
|
||||
- 域名:*.your-domain.com, your-domain.com
|
||||
|
||||
生成后会得到:
|
||||
- Origin Certificate (保存为 cloudflare-origin.pem)
|
||||
- Private Key (保存为 cloudflare-origin.key)
|
||||
```
|
||||
|
||||
#### 3. 配置 Nginx
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/designer-cep
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name your-domain.com www.your-domain.com;
|
||||
|
||||
# ✅ 使用 Cloudflare Origin Certificate
|
||||
ssl_certificate /etc/nginx/ssl/cloudflare-origin.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/cloudflare-origin.key;
|
||||
|
||||
# SSL 配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
# Cloudflare Real IP(获取真实用户 IP)
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 2400:cb00::/32;
|
||||
set_real_ip_from 2606:4700::/32;
|
||||
set_real_ip_from 2803:f800::/32;
|
||||
set_real_ip_from 2405:b500::/32;
|
||||
set_real_ip_from 2405:8100::/32;
|
||||
set_real_ip_from 2c0f:f248::/32;
|
||||
set_real_ip_from 2a06:98c0::/29;
|
||||
real_ip_header CF-Connecting-IP;
|
||||
|
||||
# 其他配置...
|
||||
}
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com www.your-domain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 安装证书
|
||||
|
||||
```bash
|
||||
# 1. 创建 SSL 目录
|
||||
sudo mkdir -p /etc/nginx/ssl
|
||||
sudo chmod 700 /etc/nginx/ssl
|
||||
|
||||
# 2. 上传证书文件(从本地上传)
|
||||
sudo nano /etc/nginx/ssl/cloudflare-origin.pem
|
||||
# 粘贴 Origin Certificate
|
||||
|
||||
sudo nano /etc/nginx/ssl/cloudflare-origin.key
|
||||
# 粘贴 Private Key
|
||||
|
||||
# 3. 设置权限
|
||||
sudo chmod 600 /etc/nginx/ssl/*
|
||||
|
||||
# 4. 测试配置
|
||||
sudo nginx -t
|
||||
|
||||
# 5. 重启 Nginx
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 证书有效期 15 年,不需要续期
|
||||
- ✅ 不需要 Certbot 验证
|
||||
- ✅ Cloudflare 和源站双重加密
|
||||
- ✅ 自动获取真实用户 IP
|
||||
|
||||
---
|
||||
|
||||
## 问题 5: 静态文件服务混乱
|
||||
|
||||
### 当前问题
|
||||
|
||||
```python
|
||||
# Server/app/main.py (第 37-55 行)
|
||||
# ❌ FastAPI 直接挂载静态文件目录
|
||||
app.mount("/download", StaticFiles(directory="archives"), name="download")
|
||||
app.mount("/shell", StaticFiles(directory=str(shell_dir), html=True), name="shell")
|
||||
app.mount("/core", StaticFiles(directory=str(designer_cache), html=True), name="core")
|
||||
```
|
||||
|
||||
```nginx
|
||||
# 部署架构说明.md (第 284-289 行)
|
||||
# ✅ 文档说让 Nginx 处理静态文件
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
```
|
||||
|
||||
**矛盾**:
|
||||
- FastAPI 挂载了静态文件 → API 和静态抢资源
|
||||
- 文档说用 Nginx 缓存 → 但请求还是先到 FastAPI
|
||||
- 大文件下载(shell.zip)性能差
|
||||
|
||||
### ✅ 修正方案
|
||||
|
||||
**明确职责**:
|
||||
- **Nginx** → 处理所有静态文件(HTML/JS/CSS/ZIP)
|
||||
- **FastAPI** → 只处理 API 请求(/api/v1/...)
|
||||
|
||||
#### 1. 修改 FastAPI(只留 API)
|
||||
|
||||
```python
|
||||
# Server/app/main.py
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware, Request
|
||||
import os
|
||||
from app.core.config import settings
|
||||
from app.api.v1 import auth, client, admin, analytics, jsx_demo
|
||||
from app.db import init_db
|
||||
|
||||
app = FastAPI(title=settings.PROJECT_NAME)
|
||||
|
||||
# 环境判断
|
||||
IS_PRODUCTION = os.getenv("ENV", "development") == "production"
|
||||
|
||||
# ========== CORS 配置 ==========
|
||||
if IS_PRODUCTION:
|
||||
origins = [
|
||||
"https://your-domain.com",
|
||||
"https://www.your-domain.com",
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 处理 CEP 环境的 Origin: null
|
||||
@app.middleware("http")
|
||||
async def cep_cors_middleware(request: Request, call_next):
|
||||
origin = request.headers.get("origin")
|
||||
if origin in ["null", None] or origin.startswith("cep://"):
|
||||
response = await call_next(request)
|
||||
response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
response.headers["Access-Control-Allow-Credentials"] = "true"
|
||||
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
|
||||
response.headers["Access-Control-Allow-Headers"] = "*"
|
||||
return response
|
||||
return await call_next(request)
|
||||
else:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# ========== API 路由 ==========
|
||||
app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["authentication"])
|
||||
app.include_router(client.router, prefix=f"{settings.API_V1_STR}/client", tags=["client"])
|
||||
app.include_router(admin.router, prefix=f"{settings.API_V1_STR}/admin", tags=["admin"])
|
||||
app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
|
||||
app.include_router(jsx_demo.router, prefix=f"{settings.API_V1_STR}/jsx_demo", tags=["jsx_demo"])
|
||||
|
||||
# ❌ 删除所有静态文件挂载
|
||||
# app.mount("/download", ...) # 删除
|
||||
# app.mount("/shell", ...) # 删除
|
||||
# app.mount("/core", ...) # 删除
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"message": "DesignerCEP API Server", "version": "1.0.0"}
|
||||
|
||||
@app.get("/health")
|
||||
def health_check():
|
||||
return {"status": "healthy"}
|
||||
|
||||
@app.on_event("startup")
|
||||
def on_startup():
|
||||
init_db()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run("app.main:app", host="127.0.0.1", port=8000, reload=True)
|
||||
```
|
||||
|
||||
#### 2. 配置 Nginx(完整版)
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/designer-cep
|
||||
|
||||
# ========== 上游 FastAPI 服务器 ==========
|
||||
upstream fastapi_backend {
|
||||
server 127.0.0.1:8000;
|
||||
}
|
||||
|
||||
# ========== HTTPS 服务器 ==========
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name your-domain.com www.your-domain.com;
|
||||
|
||||
# SSL 证书(Cloudflare Origin Certificate)
|
||||
ssl_certificate /etc/nginx/ssl/cloudflare-origin.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/cloudflare-origin.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
# Cloudflare Real IP
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 104.24.0.0/14;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
real_ip_header CF-Connecting-IP;
|
||||
|
||||
# 日志
|
||||
access_log /var/log/nginx/designer-cep-access.log;
|
||||
error_log /var/log/nginx/designer-cep-error.log;
|
||||
|
||||
# ========== API 请求(转发到 FastAPI)==========
|
||||
location /api/ {
|
||||
proxy_pass http://fastapi_backend;
|
||||
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;
|
||||
|
||||
# 超时设置
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# 禁用缓存(API 不应该缓存)
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# ========== 静态文件:Shell(在线登录页)==========
|
||||
location /shell/ {
|
||||
alias /var/www/DesignerCEP/Server/static/shell/;
|
||||
try_files $uri $uri/ /shell/index.html;
|
||||
|
||||
# HTML 文件:不缓存(保证拿到最新版本)
|
||||
location ~ \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# JS/CSS:长期缓存(文件名带 hash)
|
||||
location ~* \.(js|css)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 图片/字体:长期缓存
|
||||
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
# ========== 静态文件:Core(核心应用)==========
|
||||
location /core/ {
|
||||
alias /var/www/DesignerCEP/Server/static/core/;
|
||||
try_files $uri $uri/ =404;
|
||||
|
||||
# HTML 文件:不缓存
|
||||
location ~ \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
# JS/CSS:长期缓存
|
||||
location ~* \.(js|css)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 图片/字体:长期缓存
|
||||
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
# ========== 下载文件:Shell.zip / Core.zip ==========
|
||||
location /downloads/ {
|
||||
alias /var/www/DesignerCEP/Server/static/downloads/;
|
||||
|
||||
# 允许大文件下载
|
||||
client_max_body_size 500M;
|
||||
|
||||
# 启用断点续传
|
||||
add_header Accept-Ranges bytes;
|
||||
|
||||
# 缓存 ZIP 文件(1 天)
|
||||
expires 1d;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
# ========== 根路径 ==========
|
||||
location / {
|
||||
return 301 /shell/;
|
||||
}
|
||||
|
||||
# ========== Gzip 压缩 ==========
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
|
||||
|
||||
# ========== 安全头 ==========
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
}
|
||||
|
||||
# ========== HTTP → HTTPS 重定向 ==========
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com www.your-domain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 启用配置
|
||||
|
||||
```bash
|
||||
# 1. 测试配置
|
||||
sudo nginx -t
|
||||
|
||||
# 2. 重启 Nginx
|
||||
sudo systemctl restart nginx
|
||||
|
||||
# 3. 验证
|
||||
curl -I https://your-domain.com/shell/
|
||||
curl -I https://your-domain.com/api/v1/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 修正后的架构对比
|
||||
|
||||
### 修正前(有问题)
|
||||
|
||||
```
|
||||
CEP 扩展
|
||||
↓ Origin: file:// ❌ CORS 失败
|
||||
服务器 FastAPI
|
||||
↓
|
||||
静态文件 + API 混在一起 ❌ 性能差
|
||||
↓
|
||||
Token 在 URL ❌ 泄露风险
|
||||
```
|
||||
|
||||
### 修正后(正确)
|
||||
|
||||
```
|
||||
CEP 扩展
|
||||
↓ Origin: null ✅ CORS 中间件处理
|
||||
Nginx
|
||||
├─> /api/ → FastAPI(只处理 API)
|
||||
├─> /shell/ → 静态文件(高性能)
|
||||
├─> /core/ → 静态文件(高性能)
|
||||
└─> /downloads/ → 静态文件(支持断点续传)
|
||||
↓
|
||||
Token 在 Header ✅ Authorization: Bearer xxx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 总结:修正清单
|
||||
|
||||
### 1. 代码修改
|
||||
|
||||
- [ ] **Server/app/main.py**:
|
||||
- [ ] 添加 CEP CORS 中间件(处理 `Origin: null`)
|
||||
- [ ] 删除所有静态文件挂载
|
||||
- [ ] 添加环境判断(生产/开发)
|
||||
|
||||
- [ ] **Designer/src/config/index.ts**:
|
||||
- [ ] 生产环境 apiServer 改为 `https://your-domain.com`
|
||||
- [ ] 生产环境 shellLoginUrl 改为 `https://your-domain.com/shell/#/login`
|
||||
|
||||
- [ ] **Designer/src/api/request.ts**:
|
||||
- [ ] 确认 token 通过 `Authorization: Bearer` 传递
|
||||
- [ ] 确认 401 自动跳转登录
|
||||
|
||||
### 2. 服务器配置
|
||||
|
||||
- [ ] **Cloudflare**:
|
||||
- [ ] SSL/TLS 模式设为 "Full (Strict)"
|
||||
- [ ] 生成 Origin Certificate
|
||||
- [ ] 下载证书和私钥
|
||||
|
||||
- [ ] **Nginx**:
|
||||
- [ ] 上传 Cloudflare 证书
|
||||
- [ ] 更新 Nginx 配置(使用上面的完整配置)
|
||||
- [ ] 添加 Cloudflare Real IP 配置
|
||||
- [ ] 配置静态文件缓存策略
|
||||
- [ ] 重启 Nginx
|
||||
|
||||
### 3. 文档更新
|
||||
|
||||
- [ ] **部署架构说明.md**:
|
||||
- [ ] 删除 `file://` 的 CORS 说明
|
||||
- [ ] 添加 `Origin: null` 和 `cep://` 的处理方式
|
||||
- [ ] 删除 `localhost:8000` 的混淆说明
|
||||
- [ ] 明确 Core 从服务器加载
|
||||
- [ ] 删除 token 在 URL 的示例
|
||||
|
||||
- [ ] **部署前检查清单.md**:
|
||||
- [ ] 删除 Certbot 步骤
|
||||
- [ ] 添加 Cloudflare Origin Certificate 步骤
|
||||
|
||||
### 4. 环境变量
|
||||
|
||||
```bash
|
||||
# Server/.env
|
||||
ENV=production
|
||||
PROJECT_NAME=DesignerCEP
|
||||
API_V1_STR=/api/v1
|
||||
SECRET_KEY=your-secret-key-here
|
||||
DATABASE_URL=mysql://user:password@localhost:3306/designer_cep
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 1. CORS 测试
|
||||
|
||||
```bash
|
||||
# CEP 环境测试(Origin: null)
|
||||
curl -X OPTIONS https://your-domain.com/api/v1/client/login \
|
||||
-H "Origin: null" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-v
|
||||
|
||||
# 期望输出:
|
||||
# Access-Control-Allow-Origin: *
|
||||
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
|
||||
```
|
||||
|
||||
### 2. Token 安全测试
|
||||
|
||||
```bash
|
||||
# 检查 Nginx 日志,不应该有 token
|
||||
tail -f /var/log/nginx/designer-cep-access.log
|
||||
|
||||
# 期望:只有干净的 URL
|
||||
# GET /core/1.0.0/#/home HTTP/2.0
|
||||
# 不应该有:GET /core/1.0.0/#/home?token=xxx
|
||||
```
|
||||
|
||||
### 3. 静态文件性能测试
|
||||
|
||||
```bash
|
||||
# 测试缓存头
|
||||
curl -I https://your-domain.com/shell/assets/index-abc123.js
|
||||
|
||||
# 期望输出:
|
||||
# Cache-Control: public, immutable
|
||||
# Expires: (一年后的日期)
|
||||
|
||||
# 测试 HTML(不应该缓存)
|
||||
curl -I https://your-domain.com/shell/index.html
|
||||
|
||||
# 期望输出:
|
||||
# Cache-Control: no-cache, no-store, must-revalidate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**修正完成后,你的架构就真正可上线了!** 🎉
|
||||
|
||||
17
temp_backup/Server_redundant/tempdocs/检查命令.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# 检查 Caddy 状态
|
||||
|
||||
```bash
|
||||
# 1. 查看 Caddy 监听的端口
|
||||
ss -tlnp | grep caddy
|
||||
|
||||
# 2. 查看 Caddy 详细日志
|
||||
journalctl -u caddy -n 100 --no-pager
|
||||
|
||||
# 3. 检查 Caddyfile 格式
|
||||
caddy fmt --overwrite /etc/caddy/Caddyfile
|
||||
cat /etc/caddy/Caddyfile
|
||||
|
||||
# 4. 测试 443 端口
|
||||
curl -I -k https://localhost/
|
||||
```
|
||||
|
||||
124
temp_backup/Server_redundant/tempdocs/检测现有版本.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# 🔍 检测现有版本功能
|
||||
|
||||
## 问题场景
|
||||
|
||||
如果你的服务器上已经有部署的版本(在 `/var/www/app/`),但版本管理系统中没有记录,可以使用「检测当前版本」功能。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 使用步骤
|
||||
|
||||
### 1. 启动部署工具
|
||||
```bash
|
||||
cd AdminTool
|
||||
python deploy_tool.py
|
||||
```
|
||||
|
||||
### 2. 配置服务器信息
|
||||
在「服务器配置」区域填写:
|
||||
- 服务器地址
|
||||
- SSH 端口
|
||||
- 用户名
|
||||
- 密码
|
||||
|
||||
点击「保存配置」
|
||||
|
||||
### 3. 点击「🔍 检测当前版本」
|
||||
|
||||
位置:「版本历史管理」区域的操作按钮
|
||||
|
||||
### 4. 确认操作
|
||||
|
||||
系统会:
|
||||
- ✅ 扫描 `/var/www/app/` 目录
|
||||
- ✅ 计算文件大小
|
||||
- ✅ 获取最后修改时间
|
||||
- ✅ 创建版本记录(格式:`existing_YYYYMMDD_HHMMSS`)
|
||||
- ✅ 备份到 `/var/www/app_versions/`
|
||||
- ✅ 保存到版本管理系统
|
||||
|
||||
### 5. 查看结果
|
||||
|
||||
检测完成后:
|
||||
- 版本历史列表会显示检测到的版本
|
||||
- 该版本会被标记为 ✅ 当前版本
|
||||
- 服务器上会保存一份备份
|
||||
|
||||
---
|
||||
|
||||
## 📋 示例
|
||||
|
||||
### 检测前
|
||||
```
|
||||
版本历史列表:
|
||||
(暂无记录)
|
||||
```
|
||||
|
||||
### 点击「检测当前版本」后
|
||||
```
|
||||
版本历史列表:
|
||||
✅ existing_20231220_143025 (当前)
|
||||
部署时间: 2023-12-20 14:30:25
|
||||
大小: 12.3 MB
|
||||
备注: 检测到的现有版本(2023-12-20)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 注意事项
|
||||
|
||||
1. **不会影响线上服务**
|
||||
- 只读取文件信息
|
||||
- 创建备份副本
|
||||
- 不修改运行中的文件
|
||||
|
||||
2. **只能检测一次**
|
||||
- 如果已有版本记录,建议先查看现有记录
|
||||
- 如需重新检测,可先删除旧记录
|
||||
|
||||
3. **需要 SSH 权限**
|
||||
- 需要读取 `/var/www/app/` 目录
|
||||
- 需要写入 `/var/www/app_versions/` 目录
|
||||
|
||||
---
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q: 检测到的版本号为什么是 `existing_...` 格式?
|
||||
**A**: 为了区分:
|
||||
- `existing_YYYYMMDD_HHMMSS` = 检测到的现有版本
|
||||
- `YYYYMMDD_HHMMSS` = 通过工具部署的版本
|
||||
- `backup_YYYYMMDD_HHMMSS` = 自动备份的版本
|
||||
|
||||
### Q: 检测后可以回滚吗?
|
||||
**A**: 可以!检测后的版本和部署的版本功能完全一样,都可以回滚。
|
||||
|
||||
### Q: 如果 /var/www/app/ 为空会怎样?
|
||||
**A**: 会提示「目录为空或不存在」,不会创建记录。
|
||||
|
||||
### Q: 检测失败怎么办?
|
||||
**A**: 检查:
|
||||
1. SSH 连接是否正常
|
||||
2. `/var/www/app/` 目录是否存在
|
||||
3. 是否有读取权限
|
||||
|
||||
---
|
||||
|
||||
## 🎯 适用场景
|
||||
|
||||
1. **首次使用工具**
|
||||
- 服务器上已有部署的版本
|
||||
- 想将现有版本纳入版本管理
|
||||
|
||||
2. **迁移到新工具**
|
||||
- 之前手动部署的版本
|
||||
- 想使用版本管理功能
|
||||
|
||||
3. **重新初始化**
|
||||
- 版本记录丢失
|
||||
- 需要重新建立记录
|
||||
|
||||
---
|
||||
|
||||
**开始使用:`python deploy_tool.py`,点击「🔍 检测当前版本」** 🔍
|
||||
|
||||
152
temp_backup/Server_redundant/tempdocs/混合方案Demo说明.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# 混合方案 Demo 说明
|
||||
|
||||
## 🎯 核心思想
|
||||
|
||||
**本地 = 简单执行 | 服务器 = 复杂计算**
|
||||
|
||||
---
|
||||
|
||||
## 📋 Demo 示例
|
||||
|
||||
### 功能:创建智能配色图层
|
||||
|
||||
#### 流程:
|
||||
|
||||
```
|
||||
1️⃣ 用户点击"智能配色"按钮
|
||||
|
||||
2️⃣ 前端发送请求到服务器
|
||||
POST /api/v1/jsx_demo/calculate-color
|
||||
{
|
||||
"layer_name": "智能配色_12345",
|
||||
"base_color": "#FF6B9D"
|
||||
}
|
||||
|
||||
3️⃣ 🔒 服务器执行核心算法(客户端看不到)
|
||||
- 根据图层名称计算最佳颜色
|
||||
- 计算最佳不透明度
|
||||
- 选择最佳混合模式
|
||||
|
||||
→ 返回结果:
|
||||
{
|
||||
"r": 255,
|
||||
"g": 120,
|
||||
"b": 180,
|
||||
"opacity": 85,
|
||||
"blend_mode": "overlay"
|
||||
}
|
||||
|
||||
4️⃣ 前端接收结果,执行简单 JSX
|
||||
- 创建图层
|
||||
- 应用服务器计算的参数
|
||||
- 填充颜色
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 安全对比
|
||||
|
||||
### 当前内联方式(全在前端)
|
||||
|
||||
**前端代码:**
|
||||
```typescript
|
||||
const jsx = `
|
||||
var layer = doc.artLayers.add();
|
||||
layer.opacity = 85; // ⚠️ 算法暴露
|
||||
layer.blendMode = BlendMode.OVERLAY;
|
||||
// 复杂的颜色计算逻辑
|
||||
var r = 核心算法1(); // ⚠️ 可被看到
|
||||
var g = 核心算法2(); // ⚠️ 可被看到
|
||||
var b = 核心算法3(); // ⚠️ 可被看到
|
||||
`;
|
||||
```
|
||||
|
||||
❌ **问题:** 攻击者可以看到所有代码,复制算法
|
||||
|
||||
---
|
||||
|
||||
### 混合方式(计算在服务器)
|
||||
|
||||
**前端代码:**
|
||||
```typescript
|
||||
const colorResult = await fetch('/api/calculate-color', {...});
|
||||
const { r, g, b, opacity } = colorResult;
|
||||
|
||||
const jsx = `
|
||||
var layer = doc.artLayers.add();
|
||||
layer.opacity = ${opacity}; // ✅ 只是应用参数
|
||||
color.rgb.red = ${r}; // ✅ 只是应用参数
|
||||
color.rgb.green = ${g};
|
||||
color.rgb.blue = ${b};
|
||||
`;
|
||||
```
|
||||
|
||||
**服务器代码(客户端看不到):**
|
||||
```python
|
||||
# 🔒 核心算法在这里
|
||||
def calculate_color(layer_name, base_color):
|
||||
# 复杂的颜色理论计算
|
||||
# 机器学习模型推荐
|
||||
# 你的独家算法
|
||||
return {r, g, b, opacity, blend_mode}
|
||||
```
|
||||
|
||||
✅ **优势:** 攻击者只能看到"发送请求 → 应用结果",看不到算法
|
||||
|
||||
---
|
||||
|
||||
## 📂 新增文件(不影响现有系统)
|
||||
|
||||
```
|
||||
✅ 保留:
|
||||
Designer/src/api/jsxApi/inline/layer.ts (原有内联 JSX)
|
||||
Designer/src/api/jsxApi/inline/document.ts (原有内联 JSX)
|
||||
|
||||
➕ 新增:
|
||||
Server/app/api/v1/jsx_demo.py (服务器计算 Demo)
|
||||
Designer/src/api/jsxApi/inline/hybrid-demo.ts (混合调用 Demo)
|
||||
```
|
||||
|
||||
**两种方式并存,互不影响!**
|
||||
|
||||
---
|
||||
|
||||
## 🚀 测试 Demo
|
||||
|
||||
1. **启动后端**
|
||||
```bash
|
||||
cd Server
|
||||
python main.py
|
||||
```
|
||||
|
||||
2. **启动前端开发模式**
|
||||
```bash
|
||||
cd Designer
|
||||
npm run dev
|
||||
```
|
||||
|
||||
3. **点击"智能配色"按钮**
|
||||
- 会调用服务器计算颜色
|
||||
- 然后在本地创建图层并应用
|
||||
|
||||
4. **查看控制台**
|
||||
- 可以看到发送的参数
|
||||
- 可以看到服务器返回的结果
|
||||
- **但看不到服务器端的计算逻辑!**
|
||||
|
||||
---
|
||||
|
||||
## 💡 扩展建议
|
||||
|
||||
你可以把**任何核心功能**改成这种方式:
|
||||
|
||||
| 功能 | 本地执行 | 服务器计算 |
|
||||
|------|---------|-----------|
|
||||
| 创建图层 | ✅ 简单创建 | ❌ |
|
||||
| 智能配色 | ✅ 应用颜色 | ✅ 计算最佳颜色 |
|
||||
| AI 设计 | ✅ 应用结果 | ✅ AI 模型推理 |
|
||||
| 自动排版 | ✅ 移动元素 | ✅ 计算最佳位置 |
|
||||
| 滤镜参数 | ✅ 应用滤镜 | ✅ 计算最佳参数 |
|
||||
|
||||
**核心算法都在服务器,客户端只是"执行器"!**
|
||||
|
||||
282
temp_backup/Server_redundant/tempdocs/混合方案安全性说明.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# 混合方案安全性说明
|
||||
|
||||
## 📋 当前实现的安全特性
|
||||
|
||||
### 1️⃣ 核心算法保护 ✅
|
||||
|
||||
**问题:** 如何防止客户端看到核心算法?
|
||||
|
||||
**解决:**
|
||||
- ✅ 核心计算逻辑在服务器端执行
|
||||
- ✅ 客户端只能看到输入和输出,看不到计算过程
|
||||
- ✅ 即使客户端打开开发者工具,也无法获取算法
|
||||
|
||||
```python
|
||||
# 🔒 服务器端(客户端看不到)
|
||||
def calculate_expression(request):
|
||||
# 这里的算法逻辑客户端完全看不到
|
||||
result = eval(expression) # 可以替换成复杂的 AI 模型
|
||||
return result
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ 输入验证 ✅
|
||||
|
||||
**问题:** 如何防止恶意输入?
|
||||
|
||||
**解决:**
|
||||
- ✅ 正则表达式验证:只允许数字和基本运算符
|
||||
- ✅ 拒绝危险字符和代码注入
|
||||
|
||||
```python
|
||||
# 安全检查
|
||||
if not re.match(r'^[\d\s\+\-\*\/\(\)\.]+$', expression):
|
||||
return "非法输入"
|
||||
```
|
||||
|
||||
**可接受:** `87-98`, `100+200`, `(10*5)/2`
|
||||
**拒绝:** `__import__('os')`, `exec('code')`, `sys.exit()`
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ 详细日志 ✅
|
||||
|
||||
**功能:** 监控所有请求和响应
|
||||
|
||||
**日志内容:**
|
||||
```
|
||||
============================================================
|
||||
📥 收到计算请求
|
||||
时间: 2024-12-16 15:30:45
|
||||
表达式: 87-98
|
||||
API Key: 未提供(无鉴权模式)
|
||||
============================================================
|
||||
🛡️ 安全检查: 验证表达式格式...
|
||||
✅ 表达式格式验证通过
|
||||
🔒 开始执行核心算法...
|
||||
✅ 计算完成: 87-98 = -11
|
||||
============================================================
|
||||
📤 返回计算结果
|
||||
成功: True
|
||||
表达式: 87-98
|
||||
结果: -11.0
|
||||
消息: 计算成功: 87-98 = -11
|
||||
============================================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4️⃣ API Key 鉴权(可选)🔐
|
||||
|
||||
**当前状态:** 已准备好,但默认关闭
|
||||
|
||||
#### 如何启用 API Key 验证?
|
||||
|
||||
**步骤 1:在后端取消注释**
|
||||
|
||||
打开 `Server/app/api/v1/jsx_demo.py`,取消注释这段代码:
|
||||
|
||||
```python
|
||||
# 取消注释下面这段
|
||||
if x_api_key not in VALID_API_KEYS:
|
||||
logger.warning("❌ API Key 验证失败")
|
||||
raise HTTPException(status_code=403, detail="无效的 API Key")
|
||||
logger.info("✅ API Key 验证通过")
|
||||
```
|
||||
|
||||
**步骤 2:在前端添加 API Key**
|
||||
|
||||
打开 `Designer/src/api/jsxApi/inline/hybrid-demo.ts`:
|
||||
|
||||
```typescript
|
||||
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/calculate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': 'demo_key_123' // 添加这行
|
||||
},
|
||||
body: JSON.stringify({
|
||||
expression: layerName
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 安全级别对比
|
||||
|
||||
### ❌ 纯前端方案(不安全)
|
||||
|
||||
```typescript
|
||||
// 客户端代码(所有人都能看到)
|
||||
const result = complexAlgorithm(input); // ⚠️ 算法暴露
|
||||
const jsx = `var layer = doc.artLayers.add(); ...`;
|
||||
```
|
||||
|
||||
**风险:**
|
||||
- ❌ 核心算法完全暴露
|
||||
- ❌ 可以轻松复制算法
|
||||
- ❌ 可以绕过任何验证
|
||||
|
||||
---
|
||||
|
||||
### ✅ 混合方案(当前实现)
|
||||
|
||||
```typescript
|
||||
// 客户端只发送请求
|
||||
const result = await fetch('/calculate', { expression: '87-98' });
|
||||
// 只能看到返回的结果,看不到计算过程
|
||||
```
|
||||
|
||||
**优势:**
|
||||
- ✅ 核心算法在服务器,客户端看不到
|
||||
- ✅ 输入验证防止注入攻击
|
||||
- ✅ 详细日志监控所有请求
|
||||
|
||||
---
|
||||
|
||||
### 🔐 混合方案 + API Key(高级)
|
||||
|
||||
```typescript
|
||||
// 客户端需要提供 API Key
|
||||
const result = await fetch('/calculate', {
|
||||
headers: { 'X-API-Key': 'your_secret_key' },
|
||||
body: { expression: '87-98' }
|
||||
});
|
||||
```
|
||||
|
||||
**优势:**
|
||||
- ✅ 以上所有优势
|
||||
- ✅ 只有授权客户端可以调用
|
||||
- ✅ 可以限制每个 Key 的调用次数
|
||||
- ✅ 可以追踪谁在使用 API
|
||||
|
||||
---
|
||||
|
||||
## 💡 进一步加强建议
|
||||
|
||||
### 1. 加密传输(HTTPS)
|
||||
```bash
|
||||
# 使用 SSL 证书
|
||||
uvicorn app.main:app --ssl-keyfile=key.pem --ssl-certfile=cert.pem
|
||||
```
|
||||
|
||||
### 2. 限流(Rate Limiting)
|
||||
```python
|
||||
from slowapi import Limiter
|
||||
limiter = Limiter(key_func=get_remote_address)
|
||||
|
||||
@limiter.limit("10/minute") # 每分钟最多 10 次请求
|
||||
@router.post("/calculate")
|
||||
async def calculate_expression(...):
|
||||
...
|
||||
```
|
||||
|
||||
### 3. IP 白名单
|
||||
```python
|
||||
ALLOWED_IPS = {"127.0.0.1", "192.168.1.100"}
|
||||
|
||||
@router.post("/calculate")
|
||||
async def calculate_expression(request: Request):
|
||||
if request.client.host not in ALLOWED_IPS:
|
||||
raise HTTPException(403, "IP 未授权")
|
||||
```
|
||||
|
||||
### 4. 结果缓存
|
||||
```python
|
||||
from functools import lru_cache
|
||||
|
||||
@lru_cache(maxsize=1000)
|
||||
def calculate(expression: str):
|
||||
# 相同的表达式不重复计算
|
||||
return eval(expression)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 总结
|
||||
|
||||
| 特性 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 核心算法保护 | ✅ 已实现 | 算法在服务器端,客户端看不到 |
|
||||
| 输入验证 | ✅ 已实现 | 正则表达式过滤危险输入 |
|
||||
| 详细日志 | ✅ 已实现 | 记录所有请求和响应 |
|
||||
| API Key 鉴权 | 🔧 可选 | 默认关闭,取消注释即可启用 |
|
||||
| HTTPS 加密 | ⚠️ 推荐 | 生产环境必须启用 |
|
||||
| 限流保护 | ⚠️ 推荐 | 防止恶意刷接口 |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 测试安全性
|
||||
|
||||
### 测试 1:查看网络请求
|
||||
|
||||
1. 打开浏览器开发者工具(F12)
|
||||
2. 切换到 Network 标签
|
||||
3. 点击"智能配色"按钮
|
||||
4. 查看请求详情
|
||||
|
||||
**你只能看到:**
|
||||
- ✅ 请求 URL:`/api/v1/jsx_demo/calculate`
|
||||
- ✅ 请求参数:`{"expression": "87-98"}`
|
||||
- ✅ 响应结果:`{"success": true, "result": -11}`
|
||||
|
||||
**你看不到:**
|
||||
- ❌ 服务器端的计算逻辑
|
||||
- ❌ 核心算法代码
|
||||
- ❌ 其他用户的请求
|
||||
|
||||
---
|
||||
|
||||
### 测试 2:尝试注入攻击
|
||||
|
||||
尝试创建一个图层,名称为:`__import__('os').system('rm -rf /')`
|
||||
|
||||
**预期结果:**
|
||||
- ❌ 被拒绝
|
||||
- 📝 日志显示:`表达式包含非法字符`
|
||||
- 🛡️ 系统安全
|
||||
|
||||
---
|
||||
|
||||
### 测试 3:查看服务器日志
|
||||
|
||||
运行 Demo 后,查看后端控制台输出,应该看到详细的日志:
|
||||
|
||||
```bash
|
||||
cd Server
|
||||
python -m uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
**日志示例:**
|
||||
```
|
||||
2024-12-16 15:30:45 [INFO] ============================================================
|
||||
2024-12-16 15:30:45 [INFO] 📥 收到计算请求
|
||||
2024-12-16 15:30:45 [INFO] 时间: 2024-12-16 15:30:45
|
||||
2024-12-16 15:30:45 [INFO] 表达式: 87-98
|
||||
2024-12-16 15:30:45 [INFO] API Key: 未提供(无鉴权模式)
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 最佳实践建议
|
||||
|
||||
### 开发阶段(当前)
|
||||
- ✅ 无 API Key(方便测试)
|
||||
- ✅ 详细日志
|
||||
- ✅ HTTP 即可
|
||||
|
||||
### 生产环境
|
||||
- 🔐 启用 API Key
|
||||
- 🔒 启用 HTTPS
|
||||
- 📊 启用限流
|
||||
- 📝 日志写入文件
|
||||
- 🛡️ IP 白名单(可选)
|
||||
|
||||
---
|
||||
|
||||
**安全性总结:**
|
||||
当前方案已经实现了**核心算法保护**,客户端无法看到服务器端的计算逻辑。如需进一步加强,可以启用 API Key 验证和其他安全措施。
|
||||
|
||||
442
temp_backup/Server_redundant/tempdocs/混合架构开发框架指南.md
Normal file
@@ -0,0 +1,442 @@
|
||||
# DesignerCEP 混合架构开发框架指南
|
||||
|
||||
## 🎯 框架概述
|
||||
|
||||
这是一个**三层混合架构框架**,专为 Adobe CEP 插件设计,实现了:
|
||||
- **核心算法保护**(服务器端计算)
|
||||
- **动态更新**(Shell + Core 分离)
|
||||
- **安全鉴权**(API Key + 详细日志)
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 架构层次
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Layer 1: Shell(壳/启动器) │
|
||||
│ 职责: 登录、更新、加载 Core │
|
||||
│ 更新频率: 极低 │
|
||||
│ 位置: 本地安装(PS 扩展目录) │
|
||||
└──────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Layer 2: Core(业务核) │
|
||||
│ 职责: UI 界面、简单 JSX 执行 │
|
||||
│ 更新频率: 高(随时发布新功能) │
|
||||
│ 位置: 远程服务器 → 下载到本地缓存 │
|
||||
└──────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Layer 3: Server(后端服务器) │
|
||||
│ 职责: 核心算法、数据处理、鉴权 │
|
||||
│ 更新频率: 随时 │
|
||||
│ 位置: 远程服务器 │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 如何添加新功能
|
||||
|
||||
### 示例:添加一个"智能滤镜"功能
|
||||
|
||||
#### Step 1: 设计功能流程
|
||||
|
||||
```
|
||||
用户点击按钮
|
||||
→ Core 获取图层信息
|
||||
→ Core 发送到 Server(带 API Key)
|
||||
→ Server 计算最佳滤镜参数(🔒核心算法)
|
||||
→ Server 返回参数
|
||||
→ Core 应用滤镜到图层
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Step 2: 后端实现(Server 层)
|
||||
|
||||
**文件:** `Server/app/api/v1/jsx_demo.py`
|
||||
|
||||
```python
|
||||
class FilterRequest(BaseModel):
|
||||
"""滤镜计算请求"""
|
||||
layer_id: str
|
||||
image_data: str # Base64 编码的图层预览
|
||||
|
||||
class FilterResult(BaseModel):
|
||||
"""滤镜计算结果"""
|
||||
success: bool
|
||||
blur_radius: float
|
||||
sharpen_amount: float
|
||||
saturation: float
|
||||
message: str
|
||||
|
||||
@router.post("/calculate-filter", response_model=FilterResult)
|
||||
async def calculate_filter(
|
||||
request: FilterRequest,
|
||||
x_api_key: Optional[str] = Header(None)
|
||||
):
|
||||
"""
|
||||
🔒 服务器端智能滤镜计算
|
||||
客户端只能拿到参数,看不到算法
|
||||
"""
|
||||
|
||||
# 日志
|
||||
logger.info("="*60)
|
||||
logger.info("📥 收到滤镜计算请求")
|
||||
logger.info(f" 图层ID: {request.layer_id}")
|
||||
logger.info(f" API Key: {x_api_key}")
|
||||
logger.info("="*60)
|
||||
|
||||
# API Key 验证
|
||||
if not validate_api_key(x_api_key):
|
||||
logger.warning(f"❌ API Key 验证失败")
|
||||
raise HTTPException(status_code=403, detail="无效的 API Key")
|
||||
|
||||
logger.info("✅ API Key 验证通过")
|
||||
|
||||
try:
|
||||
# 🔒 核心算法在这里(客户端看不到)
|
||||
# 可以是 AI 模型、图像分析等
|
||||
|
||||
# 示例:根据图层 ID 计算参数
|
||||
layer_hash = sum(ord(c) for c in request.layer_id)
|
||||
|
||||
blur_radius = 2.0 + (layer_hash % 10) / 10
|
||||
sharpen_amount = 0.5 + (layer_hash % 5) / 10
|
||||
saturation = 1.0 + (layer_hash % 3) / 10
|
||||
|
||||
logger.info(f"✅ 计算完成: blur={blur_radius}, sharpen={sharpen_amount}")
|
||||
|
||||
return FilterResult(
|
||||
success=True,
|
||||
blur_radius=blur_radius,
|
||||
sharpen_amount=sharpen_amount,
|
||||
saturation=saturation,
|
||||
message="滤镜参数计算成功"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 计算失败: {str(e)}")
|
||||
return FilterResult(
|
||||
success=False,
|
||||
blur_radius=0,
|
||||
sharpen_amount=0,
|
||||
saturation=1.0,
|
||||
message=f"计算失败: {str(e)}"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Step 3: 前端实现(Core 层)
|
||||
|
||||
**文件:** `Designer/src/api/jsxApi/inline/smart-filter.ts`
|
||||
|
||||
```typescript
|
||||
import { evalInlineJSX, JSXResponse } from './utils';
|
||||
import { config } from '@/config';
|
||||
|
||||
/**
|
||||
* 智能滤镜:混合方案
|
||||
* 1. 本地获取图层信息
|
||||
* 2. 服务器计算最佳参数
|
||||
* 3. 本地应用滤镜
|
||||
*/
|
||||
export async function applySmartFilter(): Promise<JSXResponse> {
|
||||
try {
|
||||
// 1. 💻 本地获取图层信息
|
||||
const jsx = `
|
||||
try {
|
||||
if (!$.global.JSXUtils.hasDocument()) {
|
||||
return $.global.JSXUtils.stringify({ error: '没有打开的文档' });
|
||||
}
|
||||
|
||||
var doc = $.global.JSXUtils.getDocument();
|
||||
var layer = doc.activeLayer;
|
||||
|
||||
return $.global.JSXUtils.stringify({
|
||||
success: true,
|
||||
layerId: layer.id,
|
||||
layerName: layer.name
|
||||
});
|
||||
} catch (error) {
|
||||
return $.global.JSXUtils.stringify({ error: error.toString() });
|
||||
}
|
||||
`;
|
||||
|
||||
const layerResult = await evalInlineJSX(jsx);
|
||||
|
||||
if (layerResult.error || !layerResult.success) {
|
||||
return layerResult;
|
||||
}
|
||||
|
||||
// 2. 🌐 发送到服务器计算(核心算法)
|
||||
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/calculate-filter`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': 'demo_key_123' // 🔐 API Key
|
||||
},
|
||||
body: JSON.stringify({
|
||||
layer_id: layerResult.layerId,
|
||||
image_data: '' // 可选:发送图层预览
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return { error: '服务器错误' };
|
||||
}
|
||||
|
||||
const filterResult = await response.json();
|
||||
|
||||
if (!filterResult.success) {
|
||||
return { error: filterResult.message };
|
||||
}
|
||||
|
||||
// 3. 💻 本地应用滤镜参数
|
||||
const { blur_radius, sharpen_amount, saturation } = filterResult;
|
||||
|
||||
const applyJsx = `
|
||||
try {
|
||||
var doc = $.global.JSXUtils.getDocument();
|
||||
var layer = doc.activeLayer;
|
||||
|
||||
// 应用服务器计算的滤镜参数
|
||||
// 高斯模糊
|
||||
layer.applyGaussianBlur(${blur_radius});
|
||||
|
||||
// 锐化
|
||||
layer.applySharpen();
|
||||
|
||||
return $.global.JSXUtils.stringify({
|
||||
success: true,
|
||||
message: '智能滤镜应用成功'
|
||||
});
|
||||
} catch (error) {
|
||||
return $.global.JSXUtils.stringify({ error: error.toString() });
|
||||
}
|
||||
`;
|
||||
|
||||
return evalInlineJSX(applyJsx);
|
||||
|
||||
} catch (error) {
|
||||
return { error: String(error) };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Step 4: UI 界面(Core 层)
|
||||
|
||||
**文件:** `Designer/src/view/Home.vue`
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<a-button @click="handleSmartFilter">智能滤镜</a-button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { applySmartFilter } from '@/api/jsxApi/inline/smart-filter';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
const handleSmartFilter = async () => {
|
||||
try {
|
||||
Message.loading('正在计算最佳滤镜参数...');
|
||||
|
||||
const res = await applySmartFilter();
|
||||
|
||||
if (res && res.success) {
|
||||
Message.success(res.message);
|
||||
} else {
|
||||
Message.error(res?.error || '执行失败');
|
||||
}
|
||||
} catch (e: any) {
|
||||
Message.error('调用失败: ' + e.message);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Step 5: 部署新功能
|
||||
|
||||
```bash
|
||||
# 1. 构建 Core
|
||||
cd Designer
|
||||
npm run build:core
|
||||
|
||||
# 2. 发布新版本(使用自动部署脚本)
|
||||
cd ..
|
||||
python auto_deploy_core.py
|
||||
|
||||
# 3. 用户登录后会自动下载新版本
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 API Key 管理
|
||||
|
||||
### 添加新的 API Key
|
||||
|
||||
**文件:** `Server/app/core/api_keys.py`
|
||||
|
||||
```python
|
||||
VALID_KEYS: Dict[str, dict] = {
|
||||
"demo_key_123": {
|
||||
"name": "测试密钥",
|
||||
"permissions": ["calculate", "filter"], # 添加权限
|
||||
"rate_limit": 100
|
||||
},
|
||||
"customer_abc_456": { # 新客户
|
||||
"name": "客户 A",
|
||||
"permissions": ["calculate"],
|
||||
"rate_limit": 200
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 权限检查
|
||||
|
||||
```python
|
||||
@router.post("/calculate-filter")
|
||||
async def calculate_filter(request, x_api_key: Optional[str] = Header(None)):
|
||||
# 验证 Key
|
||||
if not validate_api_key(x_api_key):
|
||||
raise HTTPException(403, "无效的 API Key")
|
||||
|
||||
# 检查权限
|
||||
if not APIKeyManager.check_permission(x_api_key, "filter"):
|
||||
raise HTTPException(403, "没有滤镜功能权限")
|
||||
|
||||
# 继续处理...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 日志监控
|
||||
|
||||
所有请求都会自动记录:
|
||||
|
||||
```
|
||||
============================================================
|
||||
📥 收到滤镜计算请求
|
||||
图层ID: Layer_123
|
||||
API Key: demo_key_123
|
||||
============================================================
|
||||
✅ API Key 验证通过 | 名称: 测试密钥 | 权限: ['calculate', 'filter']
|
||||
🔒 开始执行核心算法...
|
||||
✅ 计算完成: blur=2.3, sharpen=0.7
|
||||
============================================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 扩展方向
|
||||
|
||||
### 1. AI 功能
|
||||
```python
|
||||
# Server 端
|
||||
@router.post("/ai-enhance")
|
||||
async def ai_enhance(image: str, x_api_key: str):
|
||||
# 调用 TensorFlow/PyTorch 模型
|
||||
result = ai_model.predict(image)
|
||||
return result
|
||||
```
|
||||
|
||||
### 2. 批量处理
|
||||
```python
|
||||
@router.post("/batch-process")
|
||||
async def batch_process(layers: List[str], x_api_key: str):
|
||||
results = []
|
||||
for layer_id in layers:
|
||||
result = process_layer(layer_id)
|
||||
results.append(result)
|
||||
return results
|
||||
```
|
||||
|
||||
### 3. 数据分析
|
||||
```python
|
||||
@router.post("/analyze-design")
|
||||
async def analyze_design(doc_info: dict, x_api_key: str):
|
||||
# 分析设计质量、配色方案等
|
||||
analysis = analyze_composition(doc_info)
|
||||
return analysis
|
||||
```
|
||||
|
||||
### 4. 实时协作
|
||||
```python
|
||||
@router.websocket("/ws/collaborate")
|
||||
async def collaborate(websocket: WebSocket, x_api_key: str):
|
||||
# WebSocket 实时同步
|
||||
await websocket.accept()
|
||||
# 多用户协作编辑
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 安全最佳实践
|
||||
|
||||
### ✅ 已实现
|
||||
- API Key 鉴权
|
||||
- 输入验证
|
||||
- 详细日志
|
||||
- 核心算法保护
|
||||
|
||||
### ⚠️ 生产环境建议
|
||||
- 启用 HTTPS
|
||||
- 添加限流(Rate Limiting)
|
||||
- IP 白名单
|
||||
- 定期更换 API Key
|
||||
- 数据库存储 Key(而非配置文件)
|
||||
|
||||
---
|
||||
|
||||
## 📚 开发流程总结
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[设计新功能] --> B[后端实现核心算法]
|
||||
B --> C[前端调用 API]
|
||||
C --> D[UI 界面集成]
|
||||
D --> E[测试]
|
||||
E --> F[构建 Core]
|
||||
F --> G[自动部署]
|
||||
G --> H[用户自动更新]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 框架优势
|
||||
|
||||
| 特性 | 传统方案 | 本框架 |
|
||||
|------|---------|--------|
|
||||
| 核心算法保护 | ❌ 暴露在前端 | ✅ 服务器端执行 |
|
||||
| 动态更新 | ❌ 需重装插件 | ✅ 自动下载更新 |
|
||||
| 安全鉴权 | ❌ 无验证 | ✅ API Key + 日志 |
|
||||
| 可扩展性 | ⚠️ 受限 | ✅ 易于添加功能 |
|
||||
| 开发效率 | ⚠️ 中等 | ✅ 模板化开发 |
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
这个框架提供了:
|
||||
1. **完整的三层架构**(Shell → Core → Server)
|
||||
2. **安全的算法保护**(服务器端计算)
|
||||
3. **灵活的更新机制**(动态加载 Core)
|
||||
4. **标准的开发模式**(模板化添加功能)
|
||||
5. **完善的监控日志**(追踪所有请求)
|
||||
|
||||
**适用场景:**
|
||||
- Adobe CEP 插件开发
|
||||
- 需要保护核心算法的应用
|
||||
- 需要频繁更新的软件
|
||||
- 需要鉴权和监控的服务
|
||||
|
||||
---
|
||||
|
||||
**现在你可以基于这个框架快速开发新功能了!** 🚀
|
||||
|
||||
355
temp_backup/Server_redundant/tempdocs/混合架构快速开发模板.md
Normal file
@@ -0,0 +1,355 @@
|
||||
# 混合架构快速开发模板
|
||||
|
||||
## 🚀 5 分钟添加新功能
|
||||
|
||||
### 模板代码
|
||||
|
||||
复制下面的模板,替换 `YOUR_FEATURE` 为你的功能名称。
|
||||
|
||||
---
|
||||
|
||||
## 📝 Step 1: 后端 API(3 层)
|
||||
|
||||
**文件:** `Server/app/api/v1/jsx_demo.py`
|
||||
|
||||
```python
|
||||
# ==================== 请求/响应模型 ====================
|
||||
class YourFeatureRequest(BaseModel):
|
||||
"""你的功能请求"""
|
||||
param1: str
|
||||
param2: int
|
||||
|
||||
class YourFeatureResult(BaseModel):
|
||||
"""你的功能结果"""
|
||||
success: bool
|
||||
result_data: dict
|
||||
message: str
|
||||
|
||||
# ==================== API 端点 ====================
|
||||
@router.post("/your-feature", response_model=YourFeatureResult)
|
||||
async def your_feature_endpoint(
|
||||
request: YourFeatureRequest,
|
||||
x_api_key: Optional[str] = Header(None)
|
||||
):
|
||||
"""
|
||||
🔒 服务器端核心计算
|
||||
客户端只能拿到结果,看不到算法
|
||||
"""
|
||||
|
||||
# 📝 日志:记录请求
|
||||
logger.info("="*60)
|
||||
logger.info("📥 收到请求: YOUR_FEATURE")
|
||||
logger.info(f" 参数1: {request.param1}")
|
||||
logger.info(f" 参数2: {request.param2}")
|
||||
logger.info(f" API Key: {x_api_key}")
|
||||
logger.info("="*60)
|
||||
|
||||
# 🔐 API Key 验证
|
||||
if not validate_api_key(x_api_key):
|
||||
logger.warning(f"❌ API Key 验证失败")
|
||||
raise HTTPException(status_code=403, detail="无效的 API Key")
|
||||
|
||||
key_info = get_key_info(x_api_key)
|
||||
logger.info(f"✅ API Key 验证通过 | 名称: {key_info['name']}")
|
||||
|
||||
try:
|
||||
# 🛡️ 输入验证
|
||||
logger.info("🛡️ 验证输入参数...")
|
||||
if not request.param1 or request.param2 < 0:
|
||||
logger.warning("❌ 参数验证失败")
|
||||
return YourFeatureResult(
|
||||
success=False,
|
||||
result_data={},
|
||||
message="参数无效"
|
||||
)
|
||||
logger.info("✅ 参数验证通过")
|
||||
|
||||
# 🔒 核心算法(客户端看不到)
|
||||
logger.info("🔒 开始执行核心算法...")
|
||||
|
||||
# ===== 在这里写你的核心逻辑 =====
|
||||
result = {
|
||||
"output1": f"处理结果: {request.param1}",
|
||||
"output2": request.param2 * 2
|
||||
}
|
||||
# ================================
|
||||
|
||||
logger.info(f"✅ 计算完成: {result}")
|
||||
|
||||
# 📤 返回结果
|
||||
logger.info("="*60)
|
||||
return YourFeatureResult(
|
||||
success=True,
|
||||
result_data=result,
|
||||
message="处理成功"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 处理失败: {str(e)}")
|
||||
return YourFeatureResult(
|
||||
success=False,
|
||||
result_data={},
|
||||
message=f"处理失败: {str(e)}"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Step 2: 前端 API(2 层)
|
||||
|
||||
**文件:** `Designer/src/api/jsxApi/inline/your-feature.ts`
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* YOUR_FEATURE 功能
|
||||
* 混合方案:本地执行 + 服务器计算
|
||||
*/
|
||||
|
||||
import { evalInlineJSX, JSXResponse } from './utils';
|
||||
import { config } from '@/config';
|
||||
|
||||
export async function yourFeatureFunction(
|
||||
param1: string,
|
||||
param2: number
|
||||
): Promise<JSXResponse> {
|
||||
try {
|
||||
// 1. 💻 【可选】本地获取 PS 数据
|
||||
const getDataJsx = `
|
||||
try {
|
||||
if (!$.global.JSXUtils.hasDocument()) {
|
||||
return $.global.JSXUtils.stringify({ error: '没有打开的文档' });
|
||||
}
|
||||
|
||||
var doc = $.global.JSXUtils.getDocument();
|
||||
|
||||
// 获取你需要的数据
|
||||
var layerName = doc.activeLayer ? doc.activeLayer.name : '';
|
||||
|
||||
return $.global.JSXUtils.stringify({
|
||||
success: true,
|
||||
layerName: layerName
|
||||
});
|
||||
} catch (error) {
|
||||
return $.global.JSXUtils.stringify({ error: error.toString() });
|
||||
}
|
||||
`;
|
||||
|
||||
const psData = await evalInlineJSX(getDataJsx);
|
||||
|
||||
if (psData.error) {
|
||||
return psData;
|
||||
}
|
||||
|
||||
// 2. 🌐 发送到服务器计算(核心算法)
|
||||
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/your-feature`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': 'demo_key_123' // 🔐 API Key
|
||||
},
|
||||
body: JSON.stringify({
|
||||
param1: param1,
|
||||
param2: param2
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return { error: '服务器错误' };
|
||||
}
|
||||
|
||||
const serverResult = await response.json();
|
||||
|
||||
if (!serverResult.success) {
|
||||
return { error: serverResult.message };
|
||||
}
|
||||
|
||||
// 3. 💻 【可选】使用服务器结果执行 PS 操作
|
||||
const { output1, output2 } = serverResult.result_data;
|
||||
|
||||
const applyJsx = `
|
||||
try {
|
||||
var doc = $.global.JSXUtils.getDocument();
|
||||
|
||||
// 使用服务器计算的结果
|
||||
// 示例:创建文本图层显示结果
|
||||
var textLayer = doc.artLayers.add();
|
||||
textLayer.kind = LayerKind.TEXT;
|
||||
textLayer.name = "${output1}";
|
||||
|
||||
return $.global.JSXUtils.stringify({
|
||||
success: true,
|
||||
message: '操作完成'
|
||||
});
|
||||
} catch (error) {
|
||||
return $.global.JSXUtils.stringify({ error: error.toString() });
|
||||
}
|
||||
`;
|
||||
|
||||
return evalInlineJSX(applyJsx);
|
||||
|
||||
} catch (error) {
|
||||
return { error: String(error) };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Step 3: UI 界面(2 层)
|
||||
|
||||
**文件:** `Designer/src/view/Home.vue`
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<a-button type="primary" @click="handleYourFeature">
|
||||
你的功能
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { yourFeatureFunction } from '@/api/jsxApi/inline/your-feature';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
const handleYourFeature = async () => {
|
||||
try {
|
||||
Message.loading('处理中...');
|
||||
|
||||
// 调用混合 API
|
||||
const res = await yourFeatureFunction('测试参数', 100);
|
||||
|
||||
if (res && res.success) {
|
||||
Message.success(res.message || '操作成功');
|
||||
} else {
|
||||
Message.error(res?.error || '执行失败');
|
||||
}
|
||||
} catch (e: any) {
|
||||
Message.error('调用失败: ' + e.message);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 开发流程
|
||||
|
||||
### 本地测试
|
||||
```bash
|
||||
# Terminal 1: 启动后端
|
||||
cd Server
|
||||
python -m uvicorn app.main:app --reload
|
||||
|
||||
# Terminal 2: 启动前端
|
||||
cd Designer
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 发布新版本
|
||||
```bash
|
||||
# 自动构建、打包、发布
|
||||
python auto_deploy_core.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 检查清单
|
||||
|
||||
开发新功能时,确保:
|
||||
|
||||
- [ ] 后端添加了 API Key 验证
|
||||
- [ ] 后端添加了详细日志
|
||||
- [ ] 后端添加了输入验证
|
||||
- [ ] 前端使用了正确的 API Key
|
||||
- [ ] 前端添加了错误处理
|
||||
- [ ] UI 有加载提示和错误提示
|
||||
- [ ] 测试了成功和失败的情况
|
||||
- [ ] 更新了版本号
|
||||
|
||||
---
|
||||
|
||||
## 🎯 三种常见模式
|
||||
|
||||
### 模式 1:纯服务器计算
|
||||
```
|
||||
前端输入 → 服务器计算 → 前端显示结果
|
||||
(不涉及 PS 操作)
|
||||
```
|
||||
|
||||
### 模式 2:服务器计算 + PS 应用
|
||||
```
|
||||
前端获取 PS 数据 → 服务器计算 → 前端应用到 PS
|
||||
(当前示例)
|
||||
```
|
||||
|
||||
### 模式 3:本地执行 + 服务器验证
|
||||
```
|
||||
前端执行操作 → 服务器验证权限 → 前端继续
|
||||
(需要权限控制的操作)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 快速参考
|
||||
|
||||
### 后端日志模板
|
||||
```python
|
||||
logger.info("="*60)
|
||||
logger.info("📥 收到请求")
|
||||
logger.info("✅ 验证通过")
|
||||
logger.info("🔒 开始计算")
|
||||
logger.info("✅ 计算完成")
|
||||
logger.info("="*60)
|
||||
```
|
||||
|
||||
### 前端错误处理模板
|
||||
```typescript
|
||||
try {
|
||||
Message.loading('处理中...');
|
||||
const res = await yourFunction();
|
||||
if (res?.success) {
|
||||
Message.success(res.message);
|
||||
} else {
|
||||
Message.error(res?.error || '失败');
|
||||
}
|
||||
} catch (e: any) {
|
||||
Message.error('调用失败: ' + e.message);
|
||||
}
|
||||
```
|
||||
|
||||
### JSX 模板
|
||||
```typescript
|
||||
const jsx = `
|
||||
try {
|
||||
if (!$.global.JSXUtils.hasDocument()) {
|
||||
return $.global.JSXUtils.stringify({ error: '没有打开的文档' });
|
||||
}
|
||||
|
||||
var doc = $.global.JSXUtils.getDocument();
|
||||
|
||||
// 你的 PS 操作代码
|
||||
|
||||
return $.global.JSXUtils.stringify({
|
||||
success: true,
|
||||
message: '成功'
|
||||
});
|
||||
} catch (error) {
|
||||
return $.global.JSXUtils.stringify({ error: error.toString() });
|
||||
}
|
||||
`;
|
||||
|
||||
return evalInlineJSX(jsx);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 完成!
|
||||
|
||||
现在你可以:
|
||||
1. 复制模板代码
|
||||
2. 替换功能名称
|
||||
3. 填写核心逻辑
|
||||
4. 测试
|
||||
5. 发布
|
||||
|
||||
**预计开发时间:5-15 分钟/功能** ⚡
|
||||
|
||||