移除发证单位并修正证书字体字重
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
"""remove unused issuer fields
|
||||||
|
|
||||||
|
Revision ID: 20260813_0008
|
||||||
|
Revises: 20260813_0007
|
||||||
|
Create Date: 2026-08-13
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
revision = "20260813_0008"
|
||||||
|
down_revision = "20260813_0007"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.drop_column("certificates", "issuer_name")
|
||||||
|
op.drop_column("project_courses", "default_issuer_name")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.add_column(
|
||||||
|
"project_courses",
|
||||||
|
sa.Column("default_issuer_name", sa.String(length=128), nullable=False, server_default=""),
|
||||||
|
)
|
||||||
|
op.add_column(
|
||||||
|
"certificates",
|
||||||
|
sa.Column("issuer_name", sa.String(length=128), nullable=False, server_default=""),
|
||||||
|
)
|
||||||
@@ -91,7 +91,6 @@ def create_certificate(
|
|||||||
course_start_date=payload.course_start_date,
|
course_start_date=payload.course_start_date,
|
||||||
course_end_date=payload.course_end_date,
|
course_end_date=payload.course_end_date,
|
||||||
issue_date=payload.issue_date,
|
issue_date=payload.issue_date,
|
||||||
issuer_name=project.default_issuer_name,
|
|
||||||
template_code=template_code,
|
template_code=template_code,
|
||||||
remark=payload.remark,
|
remark=payload.remark,
|
||||||
)
|
)
|
||||||
@@ -178,7 +177,6 @@ def _certificate_payload(certificate: Certificate, learner: Learner | None = Non
|
|||||||
"course_start_date": certificate.course_start_date,
|
"course_start_date": certificate.course_start_date,
|
||||||
"course_end_date": certificate.course_end_date,
|
"course_end_date": certificate.course_end_date,
|
||||||
"issue_date": certificate.issue_date,
|
"issue_date": certificate.issue_date,
|
||||||
"issuer_name": certificate.issuer_name,
|
|
||||||
"template_code": certificate.template_code,
|
"template_code": certificate.template_code,
|
||||||
"status": certificate.status,
|
"status": certificate.status,
|
||||||
"pdf_status": certificate.pdf_status,
|
"pdf_status": certificate.pdf_status,
|
||||||
@@ -249,7 +247,6 @@ def preview_certificate(
|
|||||||
"course_start_date": certificate.course_start_date.isoformat() if certificate.course_start_date else None,
|
"course_start_date": certificate.course_start_date.isoformat() if certificate.course_start_date else None,
|
||||||
"course_end_date": certificate.course_end_date.isoformat() if certificate.course_end_date else None,
|
"course_end_date": certificate.course_end_date.isoformat() if certificate.course_end_date else None,
|
||||||
"issue_date": certificate.issue_date.isoformat(),
|
"issue_date": certificate.issue_date.isoformat(),
|
||||||
"issuer_name": certificate.issuer_name,
|
|
||||||
"template_code": certificate.template_code,
|
"template_code": certificate.template_code,
|
||||||
"status": certificate.status,
|
"status": certificate.status,
|
||||||
"public_url": f"/cert/{public_token.token_value}" if public_token else "",
|
"public_url": f"/cert/{public_token.token_value}" if public_token else "",
|
||||||
|
|||||||
@@ -220,7 +220,6 @@ def confirm_import_batch(
|
|||||||
course_start_date=course_start_date,
|
course_start_date=course_start_date,
|
||||||
course_end_date=course_end_date,
|
course_end_date=course_end_date,
|
||||||
issue_date=issue_date,
|
issue_date=issue_date,
|
||||||
issuer_name=project.default_issuer_name,
|
|
||||||
template_code=batch.template_code,
|
template_code=batch.template_code,
|
||||||
remark=None,
|
remark=None,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ def create_project(
|
|||||||
default_certificate_name=payload.name,
|
default_certificate_name=payload.name,
|
||||||
default_course_name=payload.default_course_name,
|
default_course_name=payload.default_course_name,
|
||||||
default_stage_name=payload.default_stage_name,
|
default_stage_name=payload.default_stage_name,
|
||||||
default_issuer_name=payload.default_issuer_name,
|
|
||||||
default_template_code=template_code,
|
default_template_code=template_code,
|
||||||
)
|
)
|
||||||
db.add(project)
|
db.add(project)
|
||||||
@@ -61,7 +60,6 @@ def update_project(
|
|||||||
"name": project.name,
|
"name": project.name,
|
||||||
"default_course_name": project.default_course_name,
|
"default_course_name": project.default_course_name,
|
||||||
"default_stage_name": project.default_stage_name,
|
"default_stage_name": project.default_stage_name,
|
||||||
"default_issuer_name": project.default_issuer_name,
|
|
||||||
"default_template_code": project.default_template_code,
|
"default_template_code": project.default_template_code,
|
||||||
"status": project.status,
|
"status": project.status,
|
||||||
}
|
}
|
||||||
@@ -72,8 +70,6 @@ def update_project(
|
|||||||
project.default_course_name = payload.default_course_name
|
project.default_course_name = payload.default_course_name
|
||||||
if payload.default_stage_name is not None:
|
if payload.default_stage_name is not None:
|
||||||
project.default_stage_name = payload.default_stage_name
|
project.default_stage_name = payload.default_stage_name
|
||||||
if payload.default_issuer_name is not None:
|
|
||||||
project.default_issuer_name = payload.default_issuer_name
|
|
||||||
if payload.default_template_code is not None:
|
if payload.default_template_code is not None:
|
||||||
try:
|
try:
|
||||||
template_code = get_certificate_template(payload.default_template_code).code
|
template_code = get_certificate_template(payload.default_template_code).code
|
||||||
@@ -86,7 +82,6 @@ def update_project(
|
|||||||
"name": project.name,
|
"name": project.name,
|
||||||
"default_course_name": project.default_course_name,
|
"default_course_name": project.default_course_name,
|
||||||
"default_stage_name": project.default_stage_name,
|
"default_stage_name": project.default_stage_name,
|
||||||
"default_issuer_name": project.default_issuer_name,
|
|
||||||
"default_template_code": project.default_template_code,
|
"default_template_code": project.default_template_code,
|
||||||
"status": project.status,
|
"status": project.status,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,6 @@ def public_certificate_payload(db: Session, certificate: Certificate, learner: L
|
|||||||
"course_start_date": certificate.course_start_date.isoformat() if certificate.course_start_date else None,
|
"course_start_date": certificate.course_start_date.isoformat() if certificate.course_start_date else None,
|
||||||
"course_end_date": certificate.course_end_date.isoformat() if certificate.course_end_date else None,
|
"course_end_date": certificate.course_end_date.isoformat() if certificate.course_end_date else None,
|
||||||
"issue_date": certificate.issue_date.isoformat(),
|
"issue_date": certificate.issue_date.isoformat(),
|
||||||
"issuer_name": certificate.issuer_name,
|
|
||||||
"template_code": certificate.template_code,
|
"template_code": certificate.template_code,
|
||||||
"status": certificate.status,
|
"status": certificate.status,
|
||||||
"pdf_status": certificate.pdf_status,
|
"pdf_status": certificate.pdf_status,
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ def seed_defaults(db: Session, admin_username: str, admin_password: str) -> None
|
|||||||
default_certificate_name="培训结业证书",
|
default_certificate_name="培训结业证书",
|
||||||
default_course_name=name,
|
default_course_name=name,
|
||||||
default_stage_name=None,
|
default_stage_name=None,
|
||||||
default_issuer_name="本公司",
|
|
||||||
default_template_code="classic",
|
default_template_code="classic",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ class ProjectCourse(Base):
|
|||||||
default_certificate_name: Mapped[str] = mapped_column(String(128), default="培训结业证书")
|
default_certificate_name: Mapped[str] = mapped_column(String(128), default="培训结业证书")
|
||||||
default_course_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
default_course_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||||
default_stage_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
default_stage_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||||
default_issuer_name: Mapped[str] = mapped_column(String(128), default="本公司")
|
|
||||||
default_template_code: Mapped[str] = mapped_column(String(32), default="classic")
|
default_template_code: Mapped[str] = mapped_column(String(32), default="classic")
|
||||||
status: Mapped[str] = mapped_column(String(32), default="active")
|
status: Mapped[str] = mapped_column(String(32), default="active")
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||||
@@ -37,7 +36,6 @@ class Certificate(Base):
|
|||||||
course_start_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
course_start_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
||||||
course_end_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
course_end_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
||||||
issue_date: Mapped[date] = mapped_column(Date)
|
issue_date: Mapped[date] = mapped_column(Date)
|
||||||
issuer_name: Mapped[str] = mapped_column(String(128))
|
|
||||||
template_code: Mapped[str] = mapped_column(String(32), default="classic", index=True)
|
template_code: Mapped[str] = mapped_column(String(32), default="classic", index=True)
|
||||||
status: Mapped[str] = mapped_column(String(32), default="valid")
|
status: Mapped[str] = mapped_column(String(32), default="valid")
|
||||||
pdf_status: Mapped[str] = mapped_column(String(32), default="not_generated")
|
pdf_status: Mapped[str] = mapped_column(String(32), default="not_generated")
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class CertificateOut(BaseModel):
|
|||||||
course_start_date: date | None
|
course_start_date: date | None
|
||||||
course_end_date: date | None
|
course_end_date: date | None
|
||||||
issue_date: date
|
issue_date: date
|
||||||
issuer_name: str
|
|
||||||
template_code: str
|
template_code: str
|
||||||
status: str
|
status: str
|
||||||
pdf_status: str
|
pdf_status: str
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ class ProjectCourseCreate(BaseModel):
|
|||||||
default_certificate_name: str | None = Field(default=None, max_length=128)
|
default_certificate_name: str | None = Field(default=None, max_length=128)
|
||||||
default_course_name: str | None = Field(default=None, max_length=128)
|
default_course_name: str | None = Field(default=None, max_length=128)
|
||||||
default_stage_name: str | None = Field(default=None, max_length=128)
|
default_stage_name: str | None = Field(default=None, max_length=128)
|
||||||
default_issuer_name: str = Field(default="本公司", min_length=1, max_length=128)
|
|
||||||
default_template_code: str = Field(default="classic", min_length=1, max_length=32)
|
default_template_code: str = Field(default="classic", min_length=1, max_length=32)
|
||||||
|
|
||||||
@field_validator("code")
|
@field_validator("code")
|
||||||
@@ -31,7 +30,6 @@ class ProjectCourseUpdate(BaseModel):
|
|||||||
default_certificate_name: str | None = Field(default=None, max_length=128)
|
default_certificate_name: str | None = Field(default=None, max_length=128)
|
||||||
default_course_name: str | None = Field(default=None, max_length=128)
|
default_course_name: str | None = Field(default=None, max_length=128)
|
||||||
default_stage_name: str | None = Field(default=None, max_length=128)
|
default_stage_name: str | None = Field(default=None, max_length=128)
|
||||||
default_issuer_name: str | None = Field(default=None, min_length=1, max_length=128)
|
|
||||||
default_template_code: str | None = Field(default=None, min_length=1, max_length=32)
|
default_template_code: str | None = Field(default=None, min_length=1, max_length=32)
|
||||||
status: str | None = Field(default=None, pattern="^(active|disabled)$")
|
status: str | None = Field(default=None, pattern="^(active|disabled)$")
|
||||||
|
|
||||||
@@ -51,7 +49,6 @@ class ProjectCourseOut(BaseModel):
|
|||||||
default_certificate_name: str
|
default_certificate_name: str
|
||||||
default_course_name: str | None
|
default_course_name: str | None
|
||||||
default_stage_name: str | None
|
default_stage_name: str | None
|
||||||
default_issuer_name: str
|
|
||||||
default_template_code: str
|
default_template_code: str
|
||||||
status: str
|
status: str
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
|||||||
@@ -269,15 +269,32 @@ def font(size: int, kind: str = "song", bold: bool = False, scale: float = 1.0)
|
|||||||
("/System/Library/Fonts/STHeiti Light.ttc", 1),
|
("/System/Library/Fonts/STHeiti Light.ttc", 1),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
paths = choices.get(kind, choices["song"])
|
bold_choices = {
|
||||||
if bold and kind == "song":
|
"kai": [
|
||||||
paths = [
|
r"C:\Windows\Fonts\msyhbd.ttc",
|
||||||
|
r"C:\Windows\Fonts\simhei.ttf",
|
||||||
|
"/usr/share/fonts/opentype/noto/NotoSerifCJK-Bold.ttc",
|
||||||
|
("/System/Library/Fonts/Supplemental/Songti.ttc", 0),
|
||||||
|
("/System/Library/Fonts/STHeiti Medium.ttc", 1),
|
||||||
|
],
|
||||||
|
"hei": [
|
||||||
r"C:\Windows\Fonts\msyhbd.ttc",
|
r"C:\Windows\Fonts\msyhbd.ttc",
|
||||||
r"C:\Windows\Fonts\simhei.ttf",
|
r"C:\Windows\Fonts\simhei.ttf",
|
||||||
"/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc",
|
"/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc",
|
||||||
("/System/Library/Fonts/STHeiti Medium.ttc", 1),
|
("/System/Library/Fonts/STHeiti Medium.ttc", 1),
|
||||||
("/System/Library/Fonts/Hiragino Sans GB.ttc", 2),
|
("/System/Library/Fonts/Hiragino Sans GB.ttc", 2),
|
||||||
] + paths
|
],
|
||||||
|
"song": [
|
||||||
|
r"C:\Windows\Fonts\msyhbd.ttc",
|
||||||
|
r"C:\Windows\Fonts\simhei.ttf",
|
||||||
|
"/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc",
|
||||||
|
("/System/Library/Fonts/STHeiti Medium.ttc", 1),
|
||||||
|
("/System/Library/Fonts/Hiragino Sans GB.ttc", 2),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
paths = choices.get(kind, choices["song"])
|
||||||
|
if bold:
|
||||||
|
paths = bold_choices.get(kind, bold_choices["song"]) + paths
|
||||||
for item in paths:
|
for item in paths:
|
||||||
path, index = item if isinstance(item, tuple) else (item, 0)
|
path, index = item if isinstance(item, tuple) else (item, 0)
|
||||||
if Path(path).exists():
|
if Path(path).exists():
|
||||||
@@ -344,7 +361,7 @@ def draw_issue_date_numbers(
|
|||||||
x_scale: float,
|
x_scale: float,
|
||||||
y_scale: float,
|
y_scale: float,
|
||||||
) -> None:
|
) -> None:
|
||||||
text_font = font(13, "song", True, scale)
|
text_font = font(13, "song", False, scale)
|
||||||
y = 688 * y_scale
|
y = 688 * y_scale
|
||||||
for value, x in [(issue_year, 500), (issue_month, 535), (issue_day, 566)]:
|
for value, x in [(issue_year, 500), (issue_month, 535), (issue_day, 566)]:
|
||||||
draw.text((x * x_scale, y), value, fill="#111111", font=text_font, anchor="mm")
|
draw.text((x * x_scale, y), value, fill="#111111", font=text_font, anchor="mm")
|
||||||
@@ -364,8 +381,6 @@ def draw_inline(
|
|||||||
) -> float:
|
) -> float:
|
||||||
text_font = font(size, kind, bold, scale)
|
text_font = font(size, kind, bold, scale)
|
||||||
draw.text((x, y), value, fill="#111111", font=text_font, anchor="ls")
|
draw.text((x, y), value, fill="#111111", font=text_font, anchor="ls")
|
||||||
if bold:
|
|
||||||
draw.text((x + 0.45 * x_scale, y), value, fill="#111111", font=text_font, anchor="ls")
|
|
||||||
return x + draw.textlength(value, font=text_font) + gap * x_scale
|
return x + draw.textlength(value, font=text_font) + gap * x_scale
|
||||||
|
|
||||||
|
|
||||||
@@ -374,7 +389,6 @@ def draw_course_name(draw: ImageDraw.ImageDraw, value: str, x: float, y: int) ->
|
|||||||
text_font = font(27, "kai", True)
|
text_font = font(27, "kai", True)
|
||||||
if draw.textlength(value, font=text_font) <= max_width:
|
if draw.textlength(value, font=text_font) <= max_width:
|
||||||
draw.text((x, y), value, fill="#111111", font=text_font)
|
draw.text((x, y), value, fill="#111111", font=text_font)
|
||||||
draw.text((x + 0.45, y), value, fill="#111111", font=text_font)
|
|
||||||
return y
|
return y
|
||||||
|
|
||||||
start_y = y + 34
|
start_y = y + 34
|
||||||
@@ -382,7 +396,6 @@ def draw_course_name(draw: ImageDraw.ImageDraw, value: str, x: float, y: int) ->
|
|||||||
for index, line in enumerate(lines):
|
for index, line in enumerate(lines):
|
||||||
line_y = start_y + index * 34
|
line_y = start_y + index * 34
|
||||||
draw.text((185, line_y), line, fill="#111111", font=text_font)
|
draw.text((185, line_y), line, fill="#111111", font=text_font)
|
||||||
draw.text((185.45, line_y), line, fill="#111111", font=text_font)
|
|
||||||
return start_y + (len(lines) - 1) * 34
|
return start_y + (len(lines) - 1) * 34
|
||||||
|
|
||||||
|
|
||||||
@@ -409,8 +422,6 @@ def draw_inline_flow(
|
|||||||
x = line_start
|
x = line_start
|
||||||
y += line_height
|
y += line_height
|
||||||
draw.text((x, y), char, fill="#111111", font=text_font, anchor="ls")
|
draw.text((x, y), char, fill="#111111", font=text_font, anchor="ls")
|
||||||
if style == "course":
|
|
||||||
draw.text((x + 0.45 * x_scale, y), char, fill="#111111", font=text_font, anchor="ls")
|
|
||||||
x += width
|
x += width
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
<div class="meta">
|
<div class="meta">
|
||||||
<div>证书编号:{{ certificate_no }}</div>
|
<div>证书编号:{{ certificate_no }}</div>
|
||||||
<div>发证日期:{{ issue_date }}</div>
|
<div>发证日期:{{ issue_date }}</div>
|
||||||
<div>发证单位:{{ issuer_name }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% if qr_code_data_url %}<img class="qr" src="{{ qr_code_data_url }}" alt="二维码" />{% endif %}
|
{% if qr_code_data_url %}<img class="qr" src="{{ qr_code_data_url }}" alt="二维码" />{% endif %}
|
||||||
<div class="disclaimer">
|
<div class="disclaimer">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from app.services.certificate_templates import (
|
|||||||
get_certificate_template,
|
get_certificate_template,
|
||||||
list_certificate_templates,
|
list_certificate_templates,
|
||||||
)
|
)
|
||||||
from app.services.pdf import render_certificate_image
|
from app.services.pdf import font, render_certificate_image
|
||||||
|
|
||||||
|
|
||||||
def test_builtin_certificate_templates_have_assets():
|
def test_builtin_certificate_templates_have_assets():
|
||||||
@@ -31,3 +31,12 @@ def test_practice_camp_template_renders_four_dynamic_values():
|
|||||||
|
|
||||||
assert rendered.size == (3437, 2551)
|
assert rendered.size == (3437, 2551)
|
||||||
assert template.dynamic_fields == ("姓名", "课程开始日期", "课程结束日期", "发证日期")
|
assert template.dynamic_fields == ("姓名", "课程开始日期", "课程结束日期", "发证日期")
|
||||||
|
|
||||||
|
|
||||||
|
def test_certificate_bold_fonts_use_real_bold_faces():
|
||||||
|
for kind in ("kai", "song"):
|
||||||
|
regular = font(32, kind, False)
|
||||||
|
bold = font(32, kind, True)
|
||||||
|
|
||||||
|
assert regular.getname()[1] != bold.getname()[1]
|
||||||
|
assert "Bold" in bold.getname()[1]
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ def test_certificate_create_rejects_reversed_course_period():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_create_uses_project_issuer_without_manual_input():
|
def test_certificate_create_has_no_issuer_field():
|
||||||
certificate = CertificateCreate(
|
certificate = CertificateCreate(
|
||||||
learner_id=1,
|
learner_id=1,
|
||||||
project_code="DBY",
|
project_code="DBY",
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export interface PublicCertificate {
|
|||||||
course_start_date: string | null;
|
course_start_date: string | null;
|
||||||
course_end_date: string | null;
|
course_end_date: string | null;
|
||||||
issue_date: string;
|
issue_date: string;
|
||||||
issuer_name: string;
|
|
||||||
template_code: string;
|
template_code: string;
|
||||||
status: string;
|
status: string;
|
||||||
pdf_status: string;
|
pdf_status: string;
|
||||||
@@ -45,7 +44,6 @@ export interface ProjectCourse {
|
|||||||
default_certificate_name: string;
|
default_certificate_name: string;
|
||||||
default_course_name: string | null;
|
default_course_name: string | null;
|
||||||
default_stage_name: string | null;
|
default_stage_name: string | null;
|
||||||
default_issuer_name: string;
|
|
||||||
default_template_code: string;
|
default_template_code: string;
|
||||||
status: string;
|
status: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
@@ -77,7 +75,6 @@ export interface AdminCertificate {
|
|||||||
course_start_date: string | null;
|
course_start_date: string | null;
|
||||||
course_end_date: string | null;
|
course_end_date: string | null;
|
||||||
issue_date: string;
|
issue_date: string;
|
||||||
issuer_name: string;
|
|
||||||
template_code: string;
|
template_code: string;
|
||||||
status: string;
|
status: string;
|
||||||
pdf_status: string;
|
pdf_status: string;
|
||||||
|
|||||||
@@ -147,7 +147,6 @@
|
|||||||
<el-descriptions-item label="证书模板">{{ templateName(preview.template_code) }}</el-descriptions-item>
|
<el-descriptions-item label="证书模板">{{ templateName(preview.template_code) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="课程时间">{{ coursePeriodText(preview) }}</el-descriptions-item>
|
<el-descriptions-item label="课程时间">{{ coursePeriodText(preview) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="发证日期">{{ preview.issue_date }}</el-descriptions-item>
|
<el-descriptions-item label="发证日期">{{ preview.issue_date }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="发证单位">{{ preview.issuer_name }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="公开链接">
|
<el-descriptions-item label="公开链接">
|
||||||
<a :href="preview.public_url" target="_blank" rel="noreferrer">{{ preview.public_url }}</a>
|
<a :href="preview.public_url" target="_blank" rel="noreferrer">{{ preview.public_url }}</a>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<header class="page-head">
|
<header class="page-head">
|
||||||
<div>
|
<div>
|
||||||
<h2>项目管理</h2>
|
<h2>项目管理</h2>
|
||||||
<p>维护项目代码和导入时使用的证书默认信息。</p>
|
<p>维护项目代码、默认证书模板和课程信息。</p>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" @click="saveProject">{{ editingId ? "保存修改" : "新增项目" }}</el-button>
|
<el-button type="primary" @click="saveProject">{{ editingId ? "保存修改" : "新增项目" }}</el-button>
|
||||||
</header>
|
</header>
|
||||||
@@ -16,9 +16,6 @@
|
|||||||
<el-form-item label="项目名称">
|
<el-form-item label="项目名称">
|
||||||
<el-input v-model.trim="form.name" maxlength="128" />
|
<el-input v-model.trim="form.name" maxlength="128" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="默认发证单位">
|
|
||||||
<el-input v-model.trim="form.default_issuer_name" maxlength="128" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="默认证书模板">
|
<el-form-item label="默认证书模板">
|
||||||
<el-select v-model="form.default_template_code">
|
<el-select v-model="form.default_template_code">
|
||||||
<el-option v-for="item in templates" :key="item.code" :label="item.name" :value="item.code" />
|
<el-option v-for="item in templates" :key="item.code" :label="item.name" :value="item.code" />
|
||||||
@@ -51,7 +48,6 @@
|
|||||||
<el-table-column prop="name" label="项目名称" width="150" />
|
<el-table-column prop="name" label="项目名称" width="150" />
|
||||||
<el-table-column prop="default_course_name" label="课程名称" min-width="140" />
|
<el-table-column prop="default_course_name" label="课程名称" min-width="140" />
|
||||||
<el-table-column prop="default_stage_name" label="阶段名称" min-width="120" />
|
<el-table-column prop="default_stage_name" label="阶段名称" min-width="120" />
|
||||||
<el-table-column prop="default_issuer_name" label="发证单位" min-width="140" />
|
|
||||||
<el-table-column label="默认模板" min-width="150">
|
<el-table-column label="默认模板" min-width="150">
|
||||||
<template #default="{ row }">{{ templateName(row.default_template_code) }}</template>
|
<template #default="{ row }">{{ templateName(row.default_template_code) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -90,7 +86,6 @@ const form = reactive({
|
|||||||
name: "",
|
name: "",
|
||||||
default_course_name: "",
|
default_course_name: "",
|
||||||
default_stage_name: "",
|
default_stage_name: "",
|
||||||
default_issuer_name: "本公司",
|
|
||||||
default_template_code: "classic",
|
default_template_code: "classic",
|
||||||
status: "active",
|
status: "active",
|
||||||
});
|
});
|
||||||
@@ -104,7 +99,6 @@ const projects = computed(() => {
|
|||||||
item.name,
|
item.name,
|
||||||
item.default_course_name || "",
|
item.default_course_name || "",
|
||||||
item.default_stage_name || "",
|
item.default_stage_name || "",
|
||||||
item.default_issuer_name,
|
|
||||||
statusText(item.status),
|
statusText(item.status),
|
||||||
].join(" ").toLowerCase();
|
].join(" ").toLowerCase();
|
||||||
return text.includes(value);
|
return text.includes(value);
|
||||||
@@ -136,7 +130,6 @@ function editProject(row: ProjectCourse) {
|
|||||||
name: row.name,
|
name: row.name,
|
||||||
default_course_name: row.default_course_name || "",
|
default_course_name: row.default_course_name || "",
|
||||||
default_stage_name: row.default_stage_name || "",
|
default_stage_name: row.default_stage_name || "",
|
||||||
default_issuer_name: row.default_issuer_name,
|
|
||||||
default_template_code: row.default_template_code,
|
default_template_code: row.default_template_code,
|
||||||
status: row.status,
|
status: row.status,
|
||||||
});
|
});
|
||||||
@@ -149,7 +142,6 @@ function resetForm() {
|
|||||||
name: "",
|
name: "",
|
||||||
default_course_name: "",
|
default_course_name: "",
|
||||||
default_stage_name: "",
|
default_stage_name: "",
|
||||||
default_issuer_name: "本公司",
|
|
||||||
default_template_code: "classic",
|
default_template_code: "classic",
|
||||||
status: "active",
|
status: "active",
|
||||||
});
|
});
|
||||||
@@ -161,15 +153,14 @@ function projectPayload() {
|
|||||||
name: form.name,
|
name: form.name,
|
||||||
default_course_name: form.default_course_name || null,
|
default_course_name: form.default_course_name || null,
|
||||||
default_stage_name: form.default_stage_name || null,
|
default_stage_name: form.default_stage_name || null,
|
||||||
default_issuer_name: form.default_issuer_name,
|
|
||||||
default_template_code: form.default_template_code,
|
default_template_code: form.default_template_code,
|
||||||
status: form.status,
|
status: form.status,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveProject() {
|
async function saveProject() {
|
||||||
if (!form.code || !form.name || !form.default_issuer_name) {
|
if (!form.code || !form.name) {
|
||||||
ElMessage.warning("项目代码、项目名称和发证单位必填");
|
ElMessage.warning("项目代码和项目名称必填");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (editingId.value) {
|
if (editingId.value) {
|
||||||
|
|||||||
@@ -90,8 +90,6 @@
|
|||||||
<dd>{{ item.issue_date }}</dd>
|
<dd>{{ item.issue_date }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>发证单位</dt>
|
|
||||||
<dd>{{ item.issuer_name }}</dd>
|
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
@@ -132,7 +130,6 @@
|
|||||||
<div class="preview-meta">
|
<div class="preview-meta">
|
||||||
<span>证书编号:{{ current.certificate_no }}</span>
|
<span>证书编号:{{ current.certificate_no }}</span>
|
||||||
<span>发证日期:{{ current.issue_date }}</span>
|
<span>发证日期:{{ current.issue_date }}</span>
|
||||||
<span>发证单位:{{ current.issuer_name }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
@@ -26,8 +26,6 @@
|
|||||||
<dd>{{ certificate.issue_date }}</dd>
|
<dd>{{ certificate.issue_date }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>发证单位</dt>
|
|
||||||
<dd>{{ certificate.issuer_name }}</dd>
|
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
<el-button v-if="certificate.can_download_pdf" type="primary" class="download" :loading="downloading" @click="downloadPdf">
|
<el-button v-if="certificate.can_download_pdf" type="primary" class="download" :loading="downloading" @click="downloadPdf">
|
||||||
@@ -67,7 +65,7 @@ async function loadCertificate() {
|
|||||||
try {
|
try {
|
||||||
const { data } = await http.get<PublicCertificate>(`/public/certificates/token/${token}`);
|
const { data } = await http.get<PublicCertificate>(`/public/certificates/token/${token}`);
|
||||||
certificate.value = data;
|
certificate.value = data;
|
||||||
message.value = data.status === "valid" ? "该证书真实有效。" : "该证书已作废,请联系发证单位核实。";
|
message.value = data.status === "valid" ? "该证书真实有效。" : "该证书已作废,请联系证书管理方核实。";
|
||||||
messageType.value = data.status === "valid" ? "success" : "warning";
|
messageType.value = data.status === "valid" ? "success" : "warning";
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.value = error?.response?.data?.detail || "链接无效或已失效";
|
message.value = error?.response?.data?.detail || "链接无效或已失效";
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
<el-descriptions-item label="证书名称">{{ certificate.certificate_name }}</el-descriptions-item>
|
<el-descriptions-item label="证书名称">{{ certificate.certificate_name }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="课程时间">{{ coursePeriodText }}</el-descriptions-item>
|
<el-descriptions-item label="课程时间">{{ coursePeriodText }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="发证日期">{{ certificate.issue_date }}</el-descriptions-item>
|
<el-descriptions-item label="发证日期">{{ certificate.issue_date }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="发证单位">{{ certificate.issuer_name }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
@@ -39,7 +38,7 @@ async function loadCertificate() {
|
|||||||
try {
|
try {
|
||||||
const { data } = await http.get<PublicCertificate>(`/public/certificates/token/${token}`);
|
const { data } = await http.get<PublicCertificate>(`/public/certificates/token/${token}`);
|
||||||
certificate.value = data;
|
certificate.value = data;
|
||||||
message.value = data.status === "valid" ? "该证书真实有效。" : "该证书已作废,请联系发证单位核实。";
|
message.value = data.status === "valid" ? "该证书真实有效。" : "该证书已作废,请联系证书管理方核实。";
|
||||||
messageType.value = data.status === "valid" ? "success" : "warning";
|
messageType.value = data.status === "valid" ? "success" : "warning";
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.value = error?.response?.data?.detail || "链接无效或已失效";
|
message.value = error?.response?.data?.detail || "链接无效或已失效";
|
||||||
|
|||||||
Reference in New Issue
Block a user