ci: 添加 Epic UI 构建和部署工作流
- 新增 CI/CD 工作流文件,实现前端项目的自动构建和部署 - 支持 main、master 和 develop 分支的自动构建- 包含代码检出、环境安装、依赖管理、项目构建等步骤 - 实现构建产物的自动部署和 Docker 容器重启
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useNavigate} from 'react-router-dom';
|
||||
import {Select} from 'antd';
|
||||
import {Input} from 'antd';
|
||||
import type {Hero} from '@/api/index';
|
||||
import * as EpicApi from '@/api/index';
|
||||
|
||||
@@ -14,60 +14,31 @@ type ExtendedHero = Hero & {
|
||||
};
|
||||
|
||||
// 自定义样式覆盖 Ant Design 默认样式
|
||||
const selectStyles = `
|
||||
.ant-select {
|
||||
background-color: #1A1412 !important;
|
||||
border-color: #C17F59 !important;
|
||||
color: #E6B17E !important;
|
||||
}
|
||||
|
||||
.ant-select .ant-select-selector {
|
||||
background-color: #1A1412 !important;
|
||||
border-color: #C17F59 !important;
|
||||
color: #E6B17E !important;
|
||||
}
|
||||
|
||||
.ant-select-focused .ant-select-selector {
|
||||
border-color: #C17F59 !important;
|
||||
box-shadow: 0 0 0 2px rgba(193, 127, 89, 0.2) !important;
|
||||
}
|
||||
|
||||
.ant-select-dropdown {
|
||||
background-color: #1A1412 !important;
|
||||
border-color: #C17F59 !important;
|
||||
}
|
||||
|
||||
.ant-select-item {
|
||||
background-color: #1A1412 !important;
|
||||
color: #E6B17E !important;
|
||||
}
|
||||
|
||||
.ant-select-item-option-selected {
|
||||
background-color: #2A211E !important;
|
||||
color: #E6B17E !important;
|
||||
}
|
||||
|
||||
.ant-select-item-option-active {
|
||||
background-color: #2A211E !important;
|
||||
color: #E6B17E !important;
|
||||
}
|
||||
|
||||
.ant-select-selection-placeholder {
|
||||
color: #9B8579 !important;
|
||||
}
|
||||
|
||||
.ant-select-selection-item {
|
||||
color: #E6B17E !important;
|
||||
}
|
||||
|
||||
.ant-select-arrow {
|
||||
color: #E6B17E !important;
|
||||
}
|
||||
|
||||
.ant-select-clear {
|
||||
background-color: #1A1412 !important;
|
||||
color: #E6B17E !important;
|
||||
}
|
||||
const inputStyles = `
|
||||
.ant-input, .ant-input-affix-wrapper, .ant-input-affix-wrapper input {
|
||||
background-color: #1A1412 !important;
|
||||
border-color: rgba(193, 127, 89, 0.3) !important;
|
||||
color: #E6B17E !important;
|
||||
border-radius: 0.5rem !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.ant-input:focus, .ant-input-affix-wrapper:focus, .ant-input-affix-wrapper-focused, .ant-input:active {
|
||||
background-color: #1A1412 !important;
|
||||
border-color: #C17F59 !important;
|
||||
color: #E6B17E !important;
|
||||
box-shadow: 0 0 0 2px rgba(193, 127, 89, 0.2) !important;
|
||||
}
|
||||
.ant-input::placeholder, .ant-input-affix-wrapper input::placeholder {
|
||||
color: #9B8579 !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
.ant-input-clear-icon {
|
||||
color: #E6B17E !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
.ant-input-clear-icon:hover {
|
||||
color: #C17F59 !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const ELEMENTS = [
|
||||
@@ -157,15 +128,15 @@ const Characters: React.FC = () => {
|
||||
return <img src={`/pic/role/${role}.png`} alt={role} className="w-7 h-7" />;
|
||||
};
|
||||
|
||||
// 处理英雄选择
|
||||
const handleHeroSelect = (value: string) => {
|
||||
setSearchTerm(value);
|
||||
// 处理搜索输入
|
||||
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchTerm(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#1A1412] text-white font-sans relative">
|
||||
{/* 注入自定义样式 */}
|
||||
<style dangerouslySetInnerHTML={{ __html: selectStyles }} />
|
||||
<style dangerouslySetInnerHTML={{ __html: inputStyles }} />
|
||||
{/* 背景装饰 */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-[#1A1412] via-[#2A211E] to-[#1A1412]"></div>
|
||||
|
||||
@@ -179,26 +150,11 @@ const Characters: React.FC = () => {
|
||||
{/* 搜索组件 */}
|
||||
<div className="bg-gradient-to-br from-[#2A211E] to-[#1A1412] p-4 rounded-lg mb-8 border border-[#C17F59]/30 backdrop-blur-sm">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
|
||||
<Select
|
||||
showSearch
|
||||
placeholder="选择英雄名称/昵称"
|
||||
value={searchTerm || undefined}
|
||||
onChange={handleHeroSelect}
|
||||
onSearch={setSearchTerm}
|
||||
filterOption={(input, option) => {
|
||||
const heroName = option?.value as string;
|
||||
return heroName.toLowerCase().includes(input.toLowerCase());
|
||||
}}
|
||||
options={allHeroes.map(hero => ({
|
||||
value: hero.heroName,
|
||||
label: (
|
||||
<div className="flex items-center gap-2">
|
||||
<img src={hero.headImgUrl} alt={hero.heroName} className="w-6 h-6 rounded-full" />
|
||||
<span>{hero.heroName}</span>
|
||||
{hero.nickName && <span className="text-[#9B8579] text-sm">({hero.nickName})</span>}
|
||||
</div>
|
||||
)
|
||||
}))}
|
||||
<Input
|
||||
placeholder="搜索英雄名称或昵称"
|
||||
value={searchTerm}
|
||||
onChange={handleSearchChange}
|
||||
allowClear
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
@@ -246,46 +202,57 @@ const Characters: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{filteredHeroes.map((hero) => (
|
||||
<div
|
||||
key={hero.heroCode}
|
||||
className="flex items-center bg-[#23201B] rounded-xl border border-[#C17F59]/30 hover:border-[#C17F59] transition-all duration-300 shadow-md px-4 py-3 min-h-[96px] max-h-[96px] cursor-pointer backdrop-blur-sm"
|
||||
onClick={() => navigate(`/character/${hero.heroCode}`)}
|
||||
>
|
||||
{/* 头像+attr */}
|
||||
<div className="relative mr-4 flex-shrink-0">
|
||||
<img
|
||||
src={hero.headImgUrl}
|
||||
alt={hero.heroName}
|
||||
className="w-16 h-16 rounded-full border-2 border-[#C17F59] object-cover object-center"
|
||||
/>
|
||||
<div className="absolute -top-2 -right-2">
|
||||
{getElementIcon(hero.attribute)}
|
||||
</div>
|
||||
</div>
|
||||
{/* 右侧信息 */}
|
||||
<div className="flex flex-col justify-center flex-1 min-w-0">
|
||||
<div className="flex items-center mb-1">
|
||||
<span className="text-[16px] font-bold text-[#E6B17E]">{hero.heroName}</span>
|
||||
</div>
|
||||
<div className="flex items-center mb-1">
|
||||
{renderStars(hero.stars)}
|
||||
</div>
|
||||
<div className="flex items-center text-[#C17F59] text-base font-medium">
|
||||
{hero.role && (
|
||||
<>
|
||||
<span className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-[#E6B17E] mr-2">
|
||||
<img src={`/pic/role/${hero.role}.png`} alt={hero.role} className="w-6 h-6" />
|
||||
</span>
|
||||
<span className="align-middle">{ROLE_LABELS[hero.role] || hero.role}</span>
|
||||
</>
|
||||
)}
|
||||
{filteredHeroes.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{filteredHeroes.map((hero) => (
|
||||
<div
|
||||
key={hero.heroCode}
|
||||
className="flex items-center bg-[#23201B] rounded-xl border border-[#C17F59]/30 hover:border-[#C17F59] transition-all duration-300 shadow-md px-4 py-3 min-h-[96px] max-h-[96px] cursor-pointer backdrop-blur-sm"
|
||||
onClick={() => navigate(`/character/${hero.heroCode}`)}
|
||||
>
|
||||
{/* 头像+attr */}
|
||||
<div className="relative mr-4 flex-shrink-0">
|
||||
<img
|
||||
src={hero.headImgUrl}
|
||||
alt={hero.heroName}
|
||||
className="w-16 h-16 rounded-full border-2 border-[#C17F59] object-cover object-center"
|
||||
/>
|
||||
<div className="absolute -top-2 -right-2">
|
||||
{getElementIcon(hero.attribute)}
|
||||
</div>
|
||||
</div>
|
||||
{/* 右侧信息 */}
|
||||
<div className="flex flex-col justify-center flex-1 min-w-0">
|
||||
<div className="flex items-center mb-1">
|
||||
<span className="text-[16px] font-bold text-[#E6B17E]">{hero.heroName}</span>
|
||||
</div>
|
||||
<div className="flex items-center mb-1">
|
||||
{renderStars(hero.stars)}
|
||||
</div>
|
||||
<div className="flex items-center text-[#C17F59] text-base font-medium">
|
||||
{hero.role && (
|
||||
<>
|
||||
<span className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-[#E6B17E] mr-2">
|
||||
<img src={`/pic/role/${hero.role}.png`} alt={hero.role} className="w-6 h-6" />
|
||||
</span>
|
||||
<span className="align-middle">{ROLE_LABELS[hero.role] || hero.role}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-16">
|
||||
<div className="text-[#9B8579] text-xl font-medium mb-4">
|
||||
暂无匹配的角色数据
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-[#7A6B5F] text-sm">
|
||||
请尝试调整搜索条件或筛选条件
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user