修改员工界面

This commit is contained in:
2026-03-10 17:11:01 +08:00
parent dd6ff89ec4
commit b761e18eee

View File

@@ -10,10 +10,21 @@
<div class="identity-badges"> <div class="identity-badges">
<el-space :size="8"> <el-space :size="8">
<el-tag v-if="realStaffInfo" type="warning" effect="plain" round size="small"> <el-tag
<el-icon><User /></el-icon> 牛马身份: {{ realStaffInfo.name }} v-if="realStaffInfo"
type="warning"
effect="plain"
round
size="small"
>
<el-icon>
<User />
</el-icon>
牛马身份: {{ realStaffInfo.name }}
</el-tag> </el-tag>
<el-tag v-else type="danger" round size="small">未录入员工表,无权接单</el-tag> <el-tag v-else type="danger" round size="small"
>未录入员工表,无权接单</el-tag
>
</el-space> </el-space>
</div> </div>
</div> </div>
@@ -27,23 +38,46 @@
</div> </div>
<div class="status-tabs-wrapper"> <div class="status-tabs-wrapper">
<el-radio-group v-model="currentStatus" @change="handleStatusChange" class="custom-radio-group"> <el-radio-group
v-model="currentStatus"
@change="handleStatusChange"
class="custom-radio-group"
>
<el-radio-button label="未分配"> <el-radio-button label="未分配">
<el-icon><Notification /></el-icon> 待分配任务 <el-icon>
<Notification />
</el-icon>
待分配任务
</el-radio-button> </el-radio-button>
<el-radio-button label="进行中"> <el-radio-button label="进行中">
<el-icon><Loading v-if="loading" /><Timer v-else /></el-icon> 我的进行中 <el-icon>
<Loading v-if="loading" />
<Timer v-else />
</el-icon>
我的进行中
</el-radio-button> </el-radio-button>
<el-radio-button label="已完成"> <el-radio-button label="已完成">
<el-icon><CircleCheck /></el-icon> 我的已完成 <el-icon>
<CircleCheck />
</el-icon>
我的已完成
</el-radio-button> </el-radio-button>
<el-radio-button label="已完结"> <el-radio-button label="已完结">
<el-icon><Checked /></el-icon> 我的已完结 <el-icon>
<Checked />
</el-icon>
我的已完结
</el-radio-button> </el-radio-button>
</el-radio-group> </el-radio-group>
</div> </div>
<el-table :data="paginatedTickets" v-loading="loading" border style="width: 100%; margin-top: 20px" class="custom-table"> <el-table
:data="paginatedTickets"
v-loading="loading"
border
style="width: 100%; margin-top: 20px"
class="custom-table"
>
<template #empty> <template #empty>
<el-empty :image-size="120" :description="emptyText"> <el-empty :image-size="120" :description="emptyText">
<el-button type="primary" plain @click="fetchTickets">刷新列表</el-button> <el-button type="primary" plain @click="fetchTickets">刷新列表</el-button>
@@ -51,26 +85,55 @@
</template> </template>
<el-table-column prop="id" label="ID" width="70" align="center" /> <el-table-column prop="id" label="ID" width="70" align="center" />
<el-table-column prop="website" label="网站名称" min-width="150" show-overflow-tooltip /> <el-table-column
prop="website"
label="网站名称"
min-width="150"
show-overflow-tooltip
/>
<el-table-column label="数据量" width="150" align="center"> <el-table-column label="数据量" width="150" align="center">
<template #default="scope"> <template #default="scope">
<div class="cell-content-card"> <div class="cell-content-card">
<div class="data-display-area"> <div class="data-display-area">
<span v-if="scope.row.datanum && scope.row.datanum !== '0'" class="display-val">{{ scope.row.datanum }}</span> <span
v-if="scope.row.datanum && scope.row.datanum !== '0'"
class="display-val"
>{{ scope.row.datanum }}</span
>
<span v-else class="no-data-placeholder">-</span> <span v-else class="no-data-placeholder">-</span>
</div> </div>
<el-popover v-if="currentStatus !== '未分配'" placement="top" :width="200" trigger="click" @show="prepareEdit(scope.row, 'datanum')"> <el-popover
v-if="currentStatus !== '未分配'"
placement="top"
:width="200"
trigger="click"
@show="prepareEdit(scope.row, 'datanum')"
>
<template #reference> <template #reference>
<el-button type="primary" size="small" link class="edit-link-btn"> <el-button type="primary" size="small" link class="edit-link-btn">
{{ (scope.row.datanum && scope.row.datanum !== '0') ? '[ 修改数据 ]' : '[ 输入数据 ]' }} {{
scope.row.datanum && scope.row.datanum !== "0"
? "[ 修改数据 ]"
: "[ 输入数据 ]"
}}
</el-button> </el-button>
</template> </template>
<div class="popover-content"> <div class="popover-content">
<div class="pop-header">录入站点数据量</div> <div class="pop-header">录入站点数据量</div>
<el-input v-model="editValue" placeholder="请输入数值" size="small" clearable /> <el-input
v-model="editValue"
placeholder="请输入数值"
size="small"
clearable
/>
<div class="pop-footer"> <div class="pop-footer">
<el-button type="primary" size="small" @click="submitEdit(scope.row, 'datanum')">确认提交</el-button> <el-button
type="primary"
size="small"
@click="submitEdit(scope.row, 'datanum')"
>确认提交</el-button
>
</div> </div>
</div> </div>
</el-popover> </el-popover>
@@ -78,10 +141,15 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="任务进度" width="150" align="center"> <el-table-column
v-if="currentStatus === '进行中'"
label="任务进度"
width="150"
align="center"
>
<template #default="scope"> <template #default="scope">
<div class="cell-content-card"> <div class="cell-content-card">
<div class="data-display-area" style="width: 100%; padding: 0 10px;"> <div class="data-display-area" style="width: 100%; padding: 0 10px">
<el-progress <el-progress
:percentage="Number(scope.row.progress || 0)" :percentage="Number(scope.row.progress || 0)"
:status="Number(scope.row.progress) === 100 ? 'success' : ''" :status="Number(scope.row.progress) === 100 ? 'success' : ''"
@@ -90,7 +158,13 @@
striped-flow striped-flow
/> />
</div> </div>
<el-popover v-if="currentStatus === '进行中'" placement="top" :width="240" trigger="click" @show="prepareEdit(scope.row, 'progress')"> <el-popover
v-if="currentStatus === '进行中'"
placement="top"
:width="240"
trigger="click"
@show="prepareEdit(scope.row, 'progress')"
>
<template #reference> <template #reference>
<el-button type="primary" size="small" link class="edit-link-btn"> <el-button type="primary" size="small" link class="edit-link-btn">
[ 更新进度 ] [ 更新进度 ]
@@ -98,15 +172,24 @@
</template> </template>
<div class="popover-content"> <div class="popover-content">
<div class="pop-header">调整当前进度 (%)</div> <div class="pop-header">调整当前进度 (%)</div>
<el-radio-group v-model="editValue" size="small" class="progress-radio-group"> <el-radio-group
v-model="editValue"
size="small"
class="progress-radio-group"
>
<el-radio-button :label="0">0</el-radio-button> <el-radio-button :label="0">0</el-radio-button>
<el-radio-button :label="30">30</el-radio-button> <el-radio-button :label="30">30</el-radio-button>
<el-radio-button :label="50">50</el-radio-button> <el-radio-button :label="50">50</el-radio-button>
<el-radio-button :label="70">70</el-radio-button> <el-radio-button :label="70">70</el-radio-button>
<el-radio-button :label="100">100</el-radio-button> <el-radio-button :label="99">99</el-radio-button>
</el-radio-group> </el-radio-group>
<div class="pop-footer"> <div class="pop-footer">
<el-button type="primary" size="small" @click="submitEdit(scope.row, 'progress')">确定</el-button> <el-button
type="primary"
size="small"
@click="submitEdit(scope.row, 'progress')"
>确定</el-button
>
</div> </div>
</div> </div>
</el-popover> </el-popover>
@@ -120,20 +203,43 @@
<template #default="scope"> <template #default="scope">
<div class="cell-content-card"> <div class="cell-content-card">
<div class="data-display-area"> <div class="data-display-area">
<div v-if="scope.row.remark" class="display-remark" :title="scope.row.remark">{{ scope.row.remark }}</div> <div
v-if="scope.row.remark"
class="display-remark"
:title="scope.row.remark"
>
{{ scope.row.remark }}
</div>
<span v-else class="no-data-placeholder">-</span> <span v-else class="no-data-placeholder">-</span>
</div> </div>
<el-popover v-if="currentStatus !== '未分配'" placement="top" :width="250" trigger="click" @show="prepareEdit(scope.row, 'remark')"> <el-popover
v-if="currentStatus !== '未分配'"
placement="top"
:width="250"
trigger="click"
@show="prepareEdit(scope.row, 'remark')"
>
<template #reference> <template #reference>
<el-button type="warning" size="small" link class="edit-link-btn"> <el-button type="warning" size="small" link class="edit-link-btn">
{{ scope.row.remark ? '[ 修改备注 ]' : '[ 输入备注 ]' }} {{ scope.row.remark ? "[ 修改备注 ]" : "[ 输入备注 ]" }}
</el-button> </el-button>
</template> </template>
<div class="popover-content"> <div class="popover-content">
<div class="pop-header">录入工单备注</div> <div class="pop-header">录入工单备注</div>
<el-input v-model="editValue" type="textarea" :rows="3" placeholder="请输入备注" size="small" /> <el-input
v-model="editValue"
type="textarea"
:rows="3"
placeholder="请输入备注"
size="small"
/>
<div class="pop-footer"> <div class="pop-footer">
<el-button type="primary" size="small" @click="submitEdit(scope.row, 'remark')">提交保存</el-button> <el-button
type="primary"
size="small"
@click="submitEdit(scope.row, 'remark')"
>提交保存</el-button
>
</div> </div>
</div> </div>
</el-popover> </el-popover>
@@ -141,18 +247,46 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="['已完成', '已完结'].includes(currentStatus) ? '锁定日期' : '创建日期'" width="140" align="center"> <el-table-column
:label="['已完成', '已完结'].includes(currentStatus) ? '锁定日期' : '创建日期'"
width="140"
align="center"
>
<template #default="scope"> <template #default="scope">
<span class="pure-date"> <span class="pure-date">
{{ formatDateOnly(['已完成', '已完结'].includes(currentStatus) ? scope.row.lock_time : scope.row.create_time) }} {{
(["已完成", "已完结"].includes(currentStatus)
? scope.row.lock_time
: scope.row.create_time
)?.slice(0, 10) || "-"
}}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120" fixed="right" align="center"> <el-table-column label="操作" width="190" fixed="right" align="center">
<template #default="scope"> <template #default="scope">
<el-button v-if="scope.row.is_complete === '未分配'" type="primary" size="small" :disabled="!realStaffInfo" @click="handleAccept(scope.row)">立即接单</el-button> <el-button
<el-button v-if="scope.row.is_complete === '进行中' && String(scope.row.staff_id) === String(realStaffInfo?.id)" type="success" size="small" @click="handleFinish(scope.row)">完成任务</el-button> v-if="scope.row.is_complete === '未分配'"
type="primary"
size="small"
:disabled="!realStaffInfo"
@click="handleAccept(scope.row)"
>立即接单</el-button
>
<template
v-if="
scope.row.is_complete === '进行中' &&
String(scope.row.staff_id) === String(realStaffInfo?.id)
"
>
<el-button type="success" size="small" @click="handleFinish(scope.row)"
>完成任务</el-button
>
<el-button type="warning" plain size="small" @click="handleCancel(scope.row)"
>取消接单</el-button
>
</template>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -173,138 +307,227 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue' import { ref, onMounted, computed } from "vue";
import { useRouter } from 'vue-router' import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from "element-plus";
import { User, SwitchButton, Timer, Notification, CircleCheck, Checked, Loading } from '@element-plus/icons-vue' import {
import { ticketApi } from '@/api/ticket' User,
SwitchButton,
Timer,
Notification,
CircleCheck,
Checked,
Loading,
} from "@element-plus/icons-vue";
import { ticketApi } from "@/api/ticket";
const router = useRouter() const router = useRouter();
const loading = ref(false) const loading = ref(false);
const allTickets = ref([]) const allTickets = ref([]);
const currentStatus = ref('未分配') const currentStatus = ref("未分配");
const realStaffInfo = ref(null) const realStaffInfo = ref(null);
const editValue = ref('') const editValue = ref("");
const currentPage = ref(1) const currentPage = ref(1);
const pageSize = ref(15) const pageSize = ref(15);
const formatDateOnly = (timestamp) => {
if (!timestamp || timestamp === '0' || timestamp === '') return '-';
let date = !isNaN(timestamp) ? new Date(String(timestamp).length === 10 ? timestamp * 1000 : parseInt(timestamp)) : new Date(timestamp);
if (isNaN(date.getTime())) return '-';
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
}
// js计算时间 // js计算时间
const welcomeMessage = computed(() => { const welcomeMessage = computed(() => {
const hour = new Date().getHours() const hour = new Date().getHours();
if (hour < 6) return '凌晨好'; if (hour < 9) return '早上好'; if (hour < 12) return '上午好'; if (hour < 6) return "凌晨好";
if (hour < 14) return '中午好'; if (hour < 18) return '下午好'; return '晚上好' if (hour < 9) return "早上好";
}) if (hour < 12) return "上午好";
if (hour < 14) return "中午好";
if (hour < 18) return "下午好";
return "晚上好";
});
// 获取当前用户信息 // 获取当前用户信息
const currentUser = computed(() => { const currentUser = computed(() => {
const info = localStorage.getItem('user_info') || sessionStorage.getItem('user_info') const info = localStorage.getItem("user_info") || sessionStorage.getItem("user_info");
return info ? JSON.parse(info) : { id: null, username: '未知' } return info ? JSON.parse(info) : { id: null, username: "未知" };
}) });
// 获取工单信息 // 获取工单信息
const fetchTickets = async () => { const fetchTickets = async () => {
loading.value = true loading.value = true;
// 优化:不再获取所有工单,而是根据当前视图的状态和用户ID进行筛选
try { try {
const res = await ticketApi.getTickets({ page: 1, limit: 1000 }) const statusMap = {
// console.log(res) 未分配: 0,
if (res.code === 200 || res.code === true) allTickets.value = res.data.data 进行中: 1,
} catch (e) { ElMessage.error('获取列表失败') } 已完成: 2,
finally { loading.value = false } 已完结: 3,
};
const statusValue =
statusMap[currentStatus.value] !== undefined
? statusMap[currentStatus.value]
: currentStatus.value;
const params = {
status: statusValue,
limit: 1000, // 仍然获取足够的数据以支持前端分页
};
// 对于非“未分配”的视图,只拉取当前登录员工的工单
if (currentStatus.value !== "未分配") {
if (!realStaffInfo.value?.id) {
allTickets.value = []; // 如果没有员工信息,则清空列表
loading.value = false;
return;
} }
console.log("员工id", realStaffInfo.value.id);
params.staff_id = realStaffInfo.value.id;
}
const res = await ticketApi.getTickets(params);
if (res.code === 200 || res.code === true) {
allTickets.value = res.data.data || [];
} else {
allTickets.value = [];
ElMessage.error(res.msg || "获取列表失败");
}
} catch (e) {
allTickets.value = [];
ElMessage.error("获取列表失败");
} finally {
loading.value = false;
}
};
// 过滤工单 // 过滤工单
const filteredTickets = computed(() => { const filteredTickets = computed(() => {
return allTickets.value.filter(ticket => { // 优化:由于 fetchTickets 已经从后端获取了经过筛选的数据,
if (ticket.is_complete !== currentStatus.value) return false // allTickets 本身就是过滤后的列表,无需再在客户端进行过滤。
if (currentStatus.value !== '未分配') return String(ticket.staff_id) === String(realStaffInfo.value?.id) return allTickets.value;
return true });
})
})
const paginatedTickets = computed(() => { const paginatedTickets = computed(() => {
const start = (currentPage.value - 1) * pageSize.value const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value const end = start + pageSize.value;
return filteredTickets.value.slice(start, end) return filteredTickets.value.slice(start, end);
}) });
const handleSizeChange = (val) => { pageSize.value = val; currentPage.value = 1; } const handleSizeChange = (val) => {
const handleCurrentChange = (val) => { currentPage.value = val; } pageSize.value = val;
const handleStatusChange = () => { currentPage.value = 1; fetchTickets(); } currentPage.value = 1;
};
const handleCurrentChange = (val) => {
currentPage.value = val;
};
const handleStatusChange = async () => {
currentPage.value = 1;
await fetchTickets();
};
const prepareEdit = (row, field) => { editValue.value = row[field] || '' } const prepareEdit = (row, field) => {
editValue.value = row[field] || "";
};
const submitEdit = async (row, field) => { const submitEdit = async (row, field) => {
try { try {
const payload = { const payload = {
datanum: field === 'datanum' ? String(editValue.value) : String(row.datanum || ''), datanum: field === "datanum" ? String(editValue.value) : String(row.datanum || ""),
remark: field === 'remark' ? editValue.value : (row.remark || ''), remark: field === "remark" ? editValue.value : row.remark || "",
progress: field === 'progress' ? Number(editValue.value) : Number(row.progress || 0) progress:
} field === "progress" ? Number(editValue.value) : Number(row.progress || 0),
const res = await ticketApi.updateTicket(row.id, payload) };
const res = await ticketApi.updateTicket(row.id, payload);
if (res.code === 200 || res.code === true) { if (res.code === 200 || res.code === true) {
row[field] = editValue.value; row[field] = editValue.value;
ElMessage.success('更新成功'); ElMessage.success("更新成功");
document.body.click() document.body.click();
} }
} catch (e) { ElMessage.error('操作失败') } } catch (e) {
ElMessage.error("操作失败");
} }
};
// 点击接受工单 // 点击接受工单
const handleAccept = (row) => { const handleAccept = (row) => {
ElMessageBox.confirm(`确认接手工单 #${row.id}?`).then(async () => { ElMessageBox.confirm(`确认接手工单 #${row.id}?`).then(async () => {
try { try {
const res = await ticketApi.updateTicket(row.id, { staff_id: realStaffInfo.value.id, is_complete: '进行中', progress: 0 }) const res = await ticketApi.updateTicket(row.id, {
if (res.code === 200 || res.code === true) { ElMessage.success('接单成功'); fetchTickets() } staff_id: realStaffInfo.value.id,
} catch (e) { ElMessage.error('操作失败') } is_complete: "进行中",
}) progress: 0,
});
if (res.code === 200 || res.code === true) {
ElMessage.success("接单成功");
fetchTickets();
} }
} catch (e) {
ElMessage.error("操作失败");
}
});
};
// 点击完成 // 点击完成
const handleFinish = (row) => { const handleFinish = (row) => {
ElMessageBox.confirm('确认已完成任务?').then(async () => { ElMessageBox.confirm("确认已完成任务?").then(async () => {
try { try {
const currentTimestamp = Math.floor(Date.now() / 1000); const currentTimestamp = Math.floor(Date.now() / 1000);
// 关键修改:在提交时增加 progress: 100 // 关键修改:在提交时增加 progress: 100
const res = await ticketApi.updateTicket(row.id, { const res = await ticketApi.updateTicket(row.id, {
is_complete: '已完成', is_complete: "已完成",
lock_time: String(currentTimestamp), lock_time: String(currentTimestamp),
progress: 100 // 默认改为100 progress: 100, // 默认改为100
}) });
if (res.code === 200 || res.code === true) { if (res.code === 200 || res.code === true) {
ElMessage.success('任务已完成,进度已更新为100%'); ElMessage.success("任务已完成,进度已更新为100%");
fetchTickets() fetchTickets();
} }
} catch (e) { } catch (e) {
ElMessage.error('操作失败') ElMessage.error("操作失败");
} }
}) });
};
// 新增:取消接单
const handleCancel = (row) => {
ElMessageBox.confirm(
`确认要取消接手此工单 #${row.id} 吗?工单将退回未分配列表。`,
"取消确认",
{ type: "warning" }
).then(async () => {
try {
const res = await ticketApi.updateTicket(row.id, {
staff_id: null,
is_complete: "未分配",
progress: 0,
});
if (res.code === 200 || res.code === true) {
ElMessage.success("已取消接单,工单已退回!");
fetchTickets();
} else {
ElMessage.error(res.msg || "操作失败");
} }
} catch (e) {
ElMessage.error("操作失败");
}
});
};
const handleLogout = () => { const handleLogout = () => {
ElMessageBox.confirm('确定退出当前账号?', '提示', { type: 'warning' }).then(() => { ElMessageBox.confirm("确定退出当前账号?", "提示", { type: "warning" }).then(() => {
localStorage.clear(); sessionStorage.clear(); router.push('/login') localStorage.clear();
}) sessionStorage.clear();
} router.push("/login");
});
};
onMounted(async () => { onMounted(async () => {
try { try {
const res = await ticketApi.getStaffs() const res = await ticketApi.getStaffs();
if (res.code === 200 || res.code === true) { if (res.code === 200 || res.code === true) {
const match = (res.data || []).find(s => s.name === currentUser.value.username) const match = (res.data || []).find((s) => s.name === currentUser.value.username);
if (match) realStaffInfo.value = match if (match) realStaffInfo.value = match;
} }
} catch (e) {} } catch (e) {}
fetchTickets() await fetchTickets();
}) });
</script> </script>
<style scoped> <style scoped>
@@ -318,7 +541,8 @@ onMounted(async () => {
.welcome-container { .welcome-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; /* 这里统一控制欢迎语、副标题、标签之间的垂直间距 */ gap: 8px;
/* 这里统一控制欢迎语、副标题、标签之间的垂直间距 */
} }
/* 清除标题默认的垂直 margin,防止撑开间距 */ /* 清除标题默认的垂直 margin,防止撑开间距 */
@@ -331,14 +555,16 @@ onMounted(async () => {
/* 副标题紧贴标题 */ /* 副标题紧贴标题 */
.sub-text { .sub-text {
margin: 4px 0 0 0; /* 仅顶部保留微小间距 */ margin: 4px 0 0 0;
/* 仅顶部保留微小间距 */
color: #8c8c8c; color: #8c8c8c;
font-size: 13px; font-size: 13px;
} }
/* 标签组位置微调 */ /* 标签组位置微调 */
.identity-badges { .identity-badges {
margin-top: 4px; /* 让标签组靠近上方的文字 */ margin-top: 4px;
/* 让标签组靠近上方的文字 */
display: flex; display: flex;
align-items: center; align-items: center;
} }
@@ -347,42 +573,163 @@ onMounted(async () => {
:deep(.el-tag) { :deep(.el-tag) {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
border-style: dashed; /* 既然是牛马,边框可以搞成虚线更有自嘲感 */ border-style: dashed;
/* 既然是牛马,边框可以搞成虚线更有自嘲感 */
} }
.user-view-container {
padding: 24px;
background-color: #f0f2f5;
min-height: 100vh;
}
.user-view-container { padding: 24px; background-color: #f0f2f5; min-height: 100vh; } .user-header-card {
.user-header-card { background: white; padding: 24px; border-radius: 12px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; box-shadow: 0 2px 12px rgba(0,0,0,0.04); } background: white;
.greeting { margin: 0 0 8px 0; color: #1a1a1a; font-size: 24px; } padding: 24px;
.status-tabs-wrapper { background: white; padding: 8px; border-radius: 8px; display: inline-block; box-shadow: 0 2px 8px rgba(0,0,0,0.04); } border-radius: 12px;
.custom-radio-group :deep(.el-radio-button__inner) { border: none !important; border-radius: 6px !important; margin: 0 4px; padding: 10px 20px; display: flex; align-items: center; gap: 6px; } display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
}
.custom-table { border-radius: 12px; overflow: hidden; box-shadow: 0 4px 16px rgba(0,0,0,0.05); } .greeting {
.cell-content-card { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; min-height: 60px; } margin: 0 0 8px 0;
.data-display-area { min-height: 24px; display: flex; align-items: center; justify-content: center; width: 100%; } color: #1a1a1a;
.display-val { font-weight: 700; color: #409eff; font-size: 15px; } font-size: 24px;
.display-remark { font-size: 12px; color: #595959; max-width: 180px; line-height: 1.4; background: #f5f7fa; padding: 4px 10px; border-radius: 4px; text-align: center; } }
.no-data-placeholder { color: #dcdfe6; font-size: 12px; }
.pure-date { color: #606266; font-weight: 500; font-family: monospace; font-size: 14px; }
.edit-link-btn { font-size: 12px; height: auto; padding: 0; opacity: 0.7; }
.edit-link-btn:hover { opacity: 1; text-decoration: underline; }
.pagination-container { margin-top: 24px; display: flex; justify-content: flex-end; padding: 10px 20px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.04); } .status-tabs-wrapper {
background: white;
padding: 8px;
border-radius: 8px;
display: inline-block;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.progress-radio-group { display: flex; justify-content: center; margin: 10px 0; } .custom-radio-group :deep(.el-radio-button__inner) {
.progress-radio-group :deep(.el-radio-button__inner) { padding: 8px 10px !important; } border: none !important;
border-radius: 6px !important;
margin: 0 4px;
padding: 10px 20px;
display: flex;
align-items: center;
gap: 6px;
}
:deep(.el-table__row) { height: 100px; } .custom-table {
:deep(.el-table .cell) { overflow: visible !important; } border-radius: 12px;
.pop-header { font-size: 14px; font-weight: 600; margin-bottom: 12px; color: #434343; text-align: center; } overflow: hidden;
.pop-footer { margin-top: 12px; text-align: right; } box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
}
.cell-content-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
min-height: 60px;
}
.data-display-area {
min-height: 24px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
.display-val {
font-weight: 700;
color: #409eff;
font-size: 15px;
}
.display-remark {
font-size: 12px;
color: #595959;
max-width: 180px;
line-height: 1.4;
background: #f5f7fa;
padding: 4px 10px;
border-radius: 4px;
text-align: center;
}
.no-data-placeholder {
color: #dcdfe6;
font-size: 12px;
}
.pure-date {
color: #606266;
font-weight: 500;
font-family: monospace;
font-size: 14px;
}
.edit-link-btn {
font-size: 12px;
height: auto;
padding: 0;
opacity: 0.7;
}
.edit-link-btn:hover {
opacity: 1;
text-decoration: underline;
}
.pagination-container {
margin-top: 24px;
display: flex;
justify-content: flex-end;
padding: 10px 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.progress-radio-group {
display: flex;
justify-content: center;
margin: 10px 0;
}
.progress-radio-group :deep(.el-radio-button__inner) {
padding: 8px 10px !important;
}
:deep(.el-table__row) {
height: 100px;
}
:deep(.el-table .cell) {
overflow: visible !important;
}
.pop-header {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: #434343;
text-align: center;
}
.pop-footer {
margin-top: 12px;
text-align: right;
}
/* 1. 强行修正进度条内部容器的对齐方式 */ /* 1. 强行修正进度条内部容器的对齐方式 */
:deep(.el-progress-bar__innerText) { :deep(.el-progress-bar__innerText) {
display: flex !important; display: flex !important;
align-items: center !important; /* 垂直居中 */ align-items: center !important;
justify-content: center !important; /* 水平居中 */ /* 垂直居中 */
justify-content: center !important;
/* 水平居中 */
height: 100% !important; height: 100% !important;
line-height: 1 !important; line-height: 1 !important;
/* 移除默认的基线对齐 */ /* 移除默认的基线对齐 */