882 lines
21 KiB
Vue
882 lines
21 KiB
Vue
<template>
|
||
<div class="submission-container">
|
||
<div class="header">
|
||
<h3 class="title">我的投稿记录</h3>
|
||
<div class="header-buttons">
|
||
<button class="key-manage-btn" @click="showKeyManagement = true">密钥管理</button>
|
||
<button class="manage-btn" @click="handleManage">我要投稿</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 密钥管理模态框 -->
|
||
<div v-if="showKeyManagement" class="modal-overlay" @click.self="showKeyManagement = false">
|
||
<div class="modal-content key-management-modal">
|
||
<div class="modal-header">
|
||
<h3>密钥管理</h3>
|
||
<button class="close-btn" @click="showKeyManagement = false">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<button class="exchange-key-btn" @click="showExchangeKey = true">兑换密钥</button>
|
||
|
||
<div v-if="keyList.length > 0" class="key-table-wrapper">
|
||
<table class="key-table">
|
||
<thead>
|
||
<tr>
|
||
<th>密钥</th>
|
||
<th>状态</th>
|
||
<th>创建时间</th>
|
||
<th>最后使用时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="(key, index) in keyList" :key="index">
|
||
<td class="key-cell">
|
||
<div class="key-content">
|
||
<span>{{ visibleKeyIndex === index ? key.key_plaintext : '******' }}</span>
|
||
<button class="toggle-visibility" @click="toggleKeyVisibility(index)">
|
||
<svg v-if="visibleKeyIndex !== index" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="1.1">
|
||
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0"></path>
|
||
<path d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6"></path>
|
||
</svg>
|
||
<svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="1.1">
|
||
<path d="M10.585 10.587a2 2 0 0 0 2.829 2.828"></path>
|
||
<path d="M16.681 16.673a8.717 8.717 0 0 1 -4.681 1.327c-3.6 0 -6.6 -2 -9 -6c1.272 -2.12 2.712 -3.678 4.32 -4.674m2.86 -1.146a9.055 9.055 0 0 1 1.82 -.18c3.6 0 6.6 2 9 6c-.666 1.11 -1.379 2.067 -2.138 2.87"></path>
|
||
<path d="M3 3l18 18"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<span :class="key.is_valid ? 'status-valid' : 'status-invalid'">
|
||
{{ key.is_valid ? '有效' : '无效' }}
|
||
</span>
|
||
</td>
|
||
<td>{{ formatDate(key.created_at) }}</td>
|
||
<td>{{ key.last_used_at ? formatDate(key.last_used_at) : '未使用' }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div v-else-if="loading" class="loading-keys">
|
||
加载中...
|
||
</div>
|
||
<div v-else class="empty-keys">
|
||
<p>暂无密钥记录</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 兑换密钥模态框 -->
|
||
<div v-if="showExchangeKey" class="modal-overlay" @click.self="showExchangeKey = false">
|
||
<div class="modal-content exchange-key-modal">
|
||
<div class="modal-header">
|
||
<h3>兑换密钥</h3>
|
||
<button class="close-btn" @click="showExchangeKey = false">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="invite-code">邀请码</label>
|
||
<input
|
||
id="invite-code"
|
||
v-model="exchangeForm.invite_code"
|
||
type="text"
|
||
placeholder="输入邀请码"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="custom-key">自定义密钥</label>
|
||
<input
|
||
id="custom-key"
|
||
v-model="exchangeForm.key"
|
||
type="text"
|
||
placeholder="输入您的自定义密钥"
|
||
/>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button
|
||
class="submit-btn"
|
||
:disabled="!exchangeForm.invite_code || !exchangeForm.key || submitting"
|
||
@click="exchangeKey"
|
||
>
|
||
{{ submitting ? '处理中...' : '确认兑换' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="submission-table-wrapper">
|
||
<table class="submission-table" v-if="submissions.length > 0">
|
||
<thead>
|
||
<tr>
|
||
<th>标题</th>
|
||
<th>板块</th>
|
||
<th>审核状态</th>
|
||
<th>审核备注</th>
|
||
<th>投稿时间</th>
|
||
<th>状态更新时间</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="item in submissions" :key="item.id"
|
||
:class="{ 'clickable': item.article_id }"
|
||
@click="item.article_id && navigateToArticle(item.article_id)">
|
||
<td class="title-cell">{{ item.title }}</td>
|
||
<td>{{ item.section }}</td>
|
||
<td>
|
||
<span :class="getStatusClass(item.status)">{{ item.status }}</span>
|
||
</td>
|
||
<td>{{ item.note || '无' }}</td>
|
||
<td>{{ formatDate(item.created_at) }}</td>
|
||
<td>{{ formatDate(item.updated_at) }}</td>
|
||
<td>
|
||
<button
|
||
v-if="item.status === '已通过' && item.article_id"
|
||
class="edit-btn"
|
||
@click.stop="navigateToEdit(item.article_id)"
|
||
>
|
||
编辑
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="empty-state" v-else>
|
||
<p>暂无投稿记录</p>
|
||
</div>
|
||
|
||
<!-- 分页控件 -->
|
||
<div v-if="totalPages > 1" class="pagination">
|
||
<button
|
||
:disabled="currentPage === 1"
|
||
@click="loadSubmissions(currentPage - 1)"
|
||
class="page-btn"
|
||
>
|
||
上一页
|
||
</button>
|
||
<span class="page-info">{{ currentPage }} / {{ totalPages }}</span>
|
||
<button
|
||
:disabled="currentPage === totalPages"
|
||
@click="loadSubmissions(currentPage + 1)"
|
||
class="page-btn"
|
||
>
|
||
下一页
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import Cookies from 'js-cookie';
|
||
import messageBox from '../../utils/messageBox.js';
|
||
|
||
export default {
|
||
name: 'SubmissionList',
|
||
data() {
|
||
return {
|
||
submissions: [],
|
||
currentPage: 1,
|
||
totalPages: 1,
|
||
totalSubmissions: 0,
|
||
loading: false,
|
||
// 密钥管理相关数据
|
||
showKeyManagement: false,
|
||
showExchangeKey: false,
|
||
keyList: [],
|
||
visibleKeyIndex: -1, // -1表示没有可见的密钥
|
||
exchangeForm: {
|
||
invite_code: '',
|
||
key: ''
|
||
},
|
||
submitting: false
|
||
}
|
||
},
|
||
created() {
|
||
this.loadSubmissions(1);
|
||
},
|
||
methods: {
|
||
async loadSubmissions(pageNum) {
|
||
this.loading = true;
|
||
try {
|
||
const token = Cookies.get('token');
|
||
if (!token) {
|
||
console.error('未找到登录token');
|
||
return;
|
||
}
|
||
|
||
const response = await fetch('https://newfront.xn--xhq44jb2fzpc.com/user/submission', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': token,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({ page_num: pageNum })
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error('获取投稿记录失败');
|
||
}
|
||
|
||
const result = await response.json();
|
||
this.submissions = result.submissions;
|
||
this.currentPage = result.current_page;
|
||
this.totalPages = result.total_pages;
|
||
this.totalSubmissions = result.total_submissions;
|
||
|
||
} catch (error) {
|
||
console.error('获取投稿记录出错:', error);
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
|
||
// 作品管理按钮点击处理
|
||
handleManage() {
|
||
// 这里添加作品管理的入口函数逻辑
|
||
console.log('点击了作品管理按钮');
|
||
// 可以触发父组件事件或执行其他操作
|
||
this.$emit('manage-works');
|
||
},
|
||
|
||
// 跳转到文章页面
|
||
navigateToArticle(articleId) {
|
||
// window.location.href = `/article/${articleId}`;
|
||
// 或者使用Vue Router:
|
||
this.$router.push(`/article/${articleId}`);
|
||
},
|
||
|
||
// 跳转到编辑页面
|
||
navigateToEdit(articleId) {
|
||
// 检测是否为移动设备
|
||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth < 768;
|
||
|
||
if (isMobile) {
|
||
// 移动设备显示确认对话框
|
||
messageBox.confirm("建议在电脑端进行编辑以获得最佳体验。仍要继续吗?", "提示")
|
||
.then(() => {
|
||
// 用户确认,跳转到编辑页面
|
||
this.$router.push(`/edit?article=${articleId}`);
|
||
})
|
||
.catch(() => {
|
||
// 用户取消,不执行任何操作
|
||
console.log("用户取消了在移动端编辑");
|
||
});
|
||
} else {
|
||
// 电脑端直接跳转到编辑页面
|
||
this.$router.push(`/edit?article=${articleId}`);
|
||
}
|
||
},
|
||
|
||
// 格式化日期
|
||
formatDate(dateString) {
|
||
if (!dateString) return '未知';
|
||
const date = new Date(dateString);
|
||
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`;
|
||
},
|
||
|
||
// 获取状态样式类
|
||
getStatusClass(status) {
|
||
switch (status) {
|
||
case '已通过':
|
||
return 'status-approved';
|
||
case '审核中':
|
||
return 'status-pending';
|
||
case '未通过':
|
||
return 'status-rejected';
|
||
default:
|
||
return '';
|
||
}
|
||
},
|
||
|
||
// 加载密钥列表
|
||
async loadKeyList() {
|
||
this.loading = true;
|
||
try {
|
||
const token = Cookies.get('token');
|
||
if (!token) {
|
||
console.error('未找到登录token');
|
||
return;
|
||
}
|
||
|
||
const response = await fetch('https://newfront.xn--xhq44jb2fzpc.com/encrypt/getcode', {
|
||
method: 'GET',
|
||
headers: {
|
||
'Authorization': token,
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error('获取密钥列表失败');
|
||
}
|
||
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
this.keyList = result.data || [];
|
||
} else {
|
||
throw new Error(result.message || '获取密钥列表失败');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('获取密钥列表出错:', error);
|
||
messageBox.alert(error.message || '获取密钥列表失败,请稍后再试', '错误');
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
|
||
// 切换密钥可见性
|
||
toggleKeyVisibility(index) {
|
||
if (this.visibleKeyIndex === index) {
|
||
this.visibleKeyIndex = -1; // 隐藏密钥
|
||
} else {
|
||
this.visibleKeyIndex = index; // 显示指定索引的密钥
|
||
}
|
||
},
|
||
|
||
// 兑换密钥
|
||
async exchangeKey() {
|
||
if (!this.exchangeForm.invite_code || !this.exchangeForm.key) {
|
||
messageBox.alert('请输入邀请码和密钥', '提示');
|
||
return;
|
||
}
|
||
|
||
this.submitting = true;
|
||
try {
|
||
const token = Cookies.get('token');
|
||
if (!token) {
|
||
console.error('未找到登录token');
|
||
return;
|
||
}
|
||
|
||
const response = await fetch('https://newfront.xn--xhq44jb2fzpc.com/encrypt/setcode', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': token,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(this.exchangeForm)
|
||
});
|
||
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
messageBox.alert('密钥兑换成功', '提示');
|
||
this.showExchangeKey = false;
|
||
this.exchangeForm.invite_code = '';
|
||
this.exchangeForm.key = '';
|
||
// 重新加载密钥列表
|
||
this.loadKeyList();
|
||
} else {
|
||
throw new Error(result.message || '密钥兑换失败');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('密钥兑换出错:', error);
|
||
messageBox.alert(error.message || '密钥兑换失败,请稍后再试', '错误');
|
||
} finally {
|
||
this.submitting = false;
|
||
}
|
||
}
|
||
},
|
||
watch: {
|
||
showKeyManagement(val) {
|
||
if (val) {
|
||
// 当显示密钥管理模态框时,加载密钥列表
|
||
this.loadKeyList();
|
||
} else {
|
||
// 当关闭密钥管理模态框时,重置可见密钥索引
|
||
this.visibleKeyIndex = -1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.submission-container {
|
||
background-color: #ffffff;
|
||
border-radius: 12px;
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||
padding: 20px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #2d3748;
|
||
margin: 0;
|
||
}
|
||
|
||
.header-buttons {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.key-manage-btn {
|
||
background-color: #6b7280;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.key-manage-btn:hover {
|
||
background-color: #4b5563;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.manage-btn {
|
||
background-color: #3182ce;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.manage-btn:hover {
|
||
background-color: #2c5282;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.submission-table-wrapper {
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.submission-table {
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.submission-table th,
|
||
.submission-table td {
|
||
padding: 12px 16px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.submission-table th {
|
||
background-color: #f7fafc;
|
||
color: #4a5568;
|
||
font-weight: 600;
|
||
position: sticky;
|
||
top: 0;
|
||
}
|
||
|
||
.submission-table tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.submission-table tbody tr {
|
||
transition: background-color 0.2s ease;
|
||
}
|
||
|
||
.submission-table tbody tr:hover {
|
||
background-color: #f7fafc;
|
||
}
|
||
|
||
.clickable {
|
||
cursor: pointer;
|
||
position: relative;
|
||
}
|
||
|
||
.clickable:hover {
|
||
background-color: #ebf8ff !important;
|
||
}
|
||
|
||
.clickable:after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
width: 3px;
|
||
height: 100%;
|
||
background-color: #3182ce;
|
||
}
|
||
|
||
.title-cell {
|
||
max-width: 200px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.empty-state {
|
||
padding: 40px 20px;
|
||
text-align: center;
|
||
color: #718096;
|
||
font-size: 15px;
|
||
background-color: #f7fafc;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.pagination {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
margin-top: 20px;
|
||
gap: 12px;
|
||
}
|
||
|
||
.page-btn {
|
||
background-color: white;
|
||
border: 1px solid #e2e8f0;
|
||
color: #4a5568;
|
||
padding: 6px 12px;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.page-btn:hover:not(:disabled) {
|
||
background-color: #f7fafc;
|
||
border-color: #cbd5e0;
|
||
}
|
||
|
||
.page-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.page-info {
|
||
color: #4a5568;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.status-approved {
|
||
color: #38a169;
|
||
background-color: #f0fff4;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 500;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.status-pending {
|
||
color: #d69e2e;
|
||
background-color: #fffaf0;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 500;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.status-rejected {
|
||
color: #e53e3e;
|
||
background-color: #fff5f5;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 500;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.edit-btn {
|
||
background-color: #4299e1;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
padding: 4px 8px;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.edit-btn:hover {
|
||
background-color: #3182ce;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
/* 模态框样式 */
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-content {
|
||
background-color: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15);
|
||
width: 90%;
|
||
max-width: 600px;
|
||
max-height: 80vh;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.modal-header h3 {
|
||
margin: 0;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #2d3748;
|
||
}
|
||
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
color: #718096;
|
||
cursor: pointer;
|
||
transition: color 0.2s ease;
|
||
}
|
||
|
||
.close-btn:hover {
|
||
color: #4a5568;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 20px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
/* 密钥管理模态框特定样式 */
|
||
.key-management-modal {
|
||
min-height: 300px;
|
||
}
|
||
|
||
.exchange-key-btn {
|
||
background-color: #3182ce;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.exchange-key-btn:hover {
|
||
background-color: #2c5282;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.key-table-wrapper {
|
||
margin-top: 16px;
|
||
overflow-x: auto;
|
||
width: 100%;
|
||
}
|
||
|
||
.key-table {
|
||
width: 100%;
|
||
min-width: 500px;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
}
|
||
|
||
.key-table th,
|
||
.key-table td {
|
||
padding: 12px 16px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.key-table th {
|
||
background-color: #f7fafc;
|
||
color: #4a5568;
|
||
font-weight: 600;
|
||
position: sticky;
|
||
top: 0;
|
||
}
|
||
|
||
.key-content {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.toggle-visibility {
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
color: #718096;
|
||
padding: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: color 0.2s ease;
|
||
}
|
||
|
||
.toggle-visibility:hover {
|
||
color: #4a5568;
|
||
}
|
||
|
||
.status-valid {
|
||
color: #38a169;
|
||
background-color: #f0fff4;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 500;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.status-invalid {
|
||
color: #e53e3e;
|
||
background-color: #fff5f5;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 500;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.loading-keys, .empty-keys {
|
||
padding: 40px 20px;
|
||
text-align: center;
|
||
color: #718096;
|
||
font-size: 15px;
|
||
background-color: #f7fafc;
|
||
border-radius: 8px;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
/* 兑换密钥模态框样式 */
|
||
.exchange-key-modal {
|
||
max-width: 450px;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
font-weight: 500;
|
||
color: #4a5568;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.form-group input {
|
||
width: 100%;
|
||
padding: 10px 12px;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
transition: border-color 0.2s ease;
|
||
}
|
||
|
||
.form-group input:focus {
|
||
outline: none;
|
||
border-color: #3182ce;
|
||
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.15);
|
||
}
|
||
|
||
.form-actions {
|
||
margin-top: 24px;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.submit-btn {
|
||
background-color: #3182ce;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 10px 20px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.submit-btn:hover:not(:disabled) {
|
||
background-color: #2c5282;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.submit-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.submission-container {
|
||
padding: 16px;
|
||
}
|
||
|
||
.header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
}
|
||
|
||
.header-buttons {
|
||
display: flex;
|
||
width: 100%;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.key-manage-btn,
|
||
.manage-btn {
|
||
flex: 1;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 95%;
|
||
max-height: 90vh;
|
||
}
|
||
|
||
.key-table-wrapper {
|
||
margin: 0 -10px;
|
||
padding: 0 10px;
|
||
width: calc(100% + 20px);
|
||
}
|
||
|
||
.key-table th,
|
||
.key-table td {
|
||
padding: 10px 12px;
|
||
font-size: 13px;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
</style>
|