style: 适配全站灰绿配色主题,优化审批弹窗样式

- 将弹窗主题改为深色背景 (#1f1f1f)
- 标签颜色改为深灰背景+白色文字
- Radio 选中颜色和 hover 颜色改为全站主色 (#31f08b)
- 按钮和下拉框聚焦颜色统一为绿色主题
- 修复指定设计师功能点击无效的问题
This commit is contained in:
Your Name 2026-01-03 13:28:47 +08:00
parent c3454123db
commit 6113bb3b0c

View File

@ -1801,6 +1801,128 @@
</a-tabs>
</div>
<!-- 审核通过弹窗 -->
<a-modal
v-model:visible="approveModalVisible"
title="接单部门审核通过"
:width="600"
:confirm-loading="approvalLoading"
@ok="handleApproveConfirm"
class="approve-modal"
:body-style="{ padding: '24px', background: '#1f1f1f' }"
>
<!-- 任务类型编辑 -->
<div class="modal-section">
<div class="section-header" style="margin-bottom: 12px;">
<span class="section-title" style="font-weight: 600; color: #fff;">编辑任务类型</span>
</div>
<div class="task-type-list" style="background: #141414; padding: 12px; border-radius: 8px;">
<div v-for="(item, index) in approveTaskTypes" :key="index" class="task-type-item" style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; padding: 8px; background: #262626; border-radius: 4px;">
<div class="type-info">
<a-tag color="#303030" style="color: #fff; border: 1px solid #444;">{{ getTaskTypeName(item.typeId) }}</a-tag>
</div>
<div class="type-controls" style="display: flex; align-items: center;">
<span class="label" style="color: #888; margin-right: 8px; font-size: 12px;">数量</span>
<a-input-number
v-model:value="item.quantity"
:min="1"
size="small"
style="width: 60px; margin-right: 8px;"
/>
<a-button
type="text"
danger
size="small"
@click="approveTaskTypes.splice(index, 1)"
>
<Icon name="trash-2" size="sm" />
</a-button>
</div>
</div>
<!-- 添加类型 -->
<div class="add-type-row" style="margin-top: 12px; display: flex; gap: 8px;">
<a-select
v-model:value="newTaskTypeId"
placeholder="选择要添加的任务类型"
style="flex: 1"
allow-clear
:dropdown-style="{ background: '#262626' }"
>
<a-select-option v-for="type in demandTypes" :key="type.id" :value="type.id">
{{ type.name }}
</a-select-option>
</a-select>
<a-button type="primary" ghost style="border-color: #31f08b; color: #31f08b;" @click="() => {
if (newTaskTypeId && !approveTaskTypes.find(t => t.typeId === newTaskTypeId)) {
approveTaskTypes.push({ typeId: newTaskTypeId, quantity: 1 })
newTaskTypeId = undefined
} else if (newTaskTypeId) {
message.warning('该任务类型已添加')
}
}">
<Icon name="plus" size="sm" style="margin-right: 4px" /> 添加
</a-button>
</div>
</div>
</div>
<!-- 接单方式 -->
<div class="modal-section" style="margin-top: 24px;">
<div class="section-header" style="margin-bottom: 12px;">
<span class="section-title" style="font-weight: 600; color: #fff;">选择接单方式</span>
</div>
<a-radio-group v-model:value="approveAssignType" class="assign-type-group" style="width: 100%;">
<a-radio value="auto" class="assign-option" style="display: flex; width: 100%; margin-bottom: 12px; padding: 16px; border: 1px solid #303030; border-radius: 8px; background: #141414; align-items: center;">
<div class="option-content" style="display: inline-flex; flex-direction: column; margin-left: 8px;">
<span class="option-title" style="font-weight: 500; color: #fff; font-size: 14px;">发布到任务大厅</span>
<span class="option-desc" style="font-size: 12px; color: #888; margin-top: 4px;">所有设计师均可抢单</span>
</div>
</a-radio>
<a-radio value="manual" class="assign-option" style="display: flex; width: 100%; padding: 16px; border: 1px solid #303030; border-radius: 8px; background: #141414; align-items: center;">
<div class="option-content" style="display: inline-flex; flex-direction: column; margin-left: 8px;">
<span class="option-title" style="font-weight: 500; color: #fff; font-size: 14px;">指定设计师接单</span>
<span class="option-desc" style="font-size: 12px; color: #888; margin-top: 4px;">直接分配给特定设计师</span>
</div>
</a-radio>
</a-radio-group>
</div>
<!-- 选择设计师 -->
<div v-if="approveAssignType === 'manual'" class="modal-section" style="margin-top: 24px;">
<div class="section-header" style="margin-bottom: 12px;">
<span class="section-title" style="font-weight: 600; color: #fff;">选择设计师</span>
</div>
<a-select
v-model:value="approveDesignerId"
placeholder="请选择设计师"
style="width: 100%"
size="large"
option-label-prop="label"
:dropdown-style="{ background: '#1f1f1f', borderColor: '#303030' }"
>
<a-select-option
v-for="designer in designersList"
:key="designer.id"
:value="designer.id"
:label="designer.realName"
>
<div class="designer-option" style="display: flex; justify-content: space-between; align-items: center; padding: 4px 0;">
<span class="designer-name" style="color: #fff; font-weight: 500;">{{ designer.realName }}</span>
<div class="designer-stats">
<a-tag :color="designer.inProgressCount === 0 ? 'success' : designer.inProgressCount > 5 ? 'error' : 'warning'">
进行中: {{ designer.inProgressCount }}
</a-tag>
<span v-if="designer.earliestDeadline" style="font-size: 12px; color: #888; margin-left: 8px;">
最早完成: {{ formatDeadlineRelative(designer.earliestDeadline) }}
</span>
</div>
</div>
</a-select-option>
</a-select>
</div>
</a-modal>
<!-- 任务详情抽屉 -->
<a-drawer
v-model:visible="detailDrawerVisible"
@ -2538,6 +2660,33 @@ const handleApproveTwoLevelTask = async (task: Task) => {
}
}
// 审批弹窗状态
const approveModalVisible = ref(false)
const approveTaskData = ref<Task | null>(null)
const approveAssignType = ref<'auto' | 'manual'>('auto')
const approveTaskTypes = ref<Array<{typeId: number, quantity: number}>>([])
const approveDesignerId = ref<number | undefined>(undefined)
const designersList = ref<DesignerWithWorkload[]>([])
const newTaskTypeId = ref<number | undefined>(undefined)
// 格式化相对时间
const formatDeadlineRelative = (deadline: string) => {
if (!deadline) return ''
try {
const date = new Date(deadline)
const now = new Date()
const diff = date.getTime() - now.getTime()
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
if (diff < 0) return '已逾期'
if (days === 0) return `${hours}小时后`
return `${days}天${hours}小时后`
} catch {
return ''
}
}
// 显示指定设计师弹窗(接单部门审核时)
const showAssignDesignerModal = async (task: Task) => {
try {
@ -2556,338 +2705,83 @@ const showAssignDesignerModal = async (task: Task) => {
message.warning('当前部门没有可用的设计师')
return
}
let assignType = 'auto' // 'auto' 或 'manual'
let selectedDesignerId: number | null = null
designersList.value = designers
approveTaskData.value = task
approveAssignType.value = 'auto'
approveDesignerId.value = undefined
newTaskTypeId.value = undefined
// 解析任务类型
const taskTypeIdsStr = task.taskTypeIds || task.taskTypeId?.toString() || ''
const taskTypeIdArray = taskTypeIdsStr.split(',').filter(id => id.trim())
// 初始化任务类型列表(带数量)
let editableTaskTypes = taskTypeIdArray.map(id => ({
// 初始化任务类型列表
approveTaskTypes.value = taskTypeIdArray.map(id => ({
typeId: parseInt(id.trim()),
quantity: task.quantity || 1
}))
let newTaskTypeId: number | null = null
const modalContent = h('div', { style: 'padding: 10px 0;' }, [
// 任务类型编辑区域
h('div', {
style: {
marginBottom: '24px',
padding: '16px',
background: '#fafafa',
borderRadius: '8px',
border: '1px solid #e8e8e8'
}
}, [
h('div', { style: 'marginBottom: 16px; display: flex; alignItems: center;' }, [
h('span', {
style: {
fontSize: '14px',
fontWeight: 600,
color: '#1890ff',
marginRight: '8px'
}
}, '📝'),
h('span', {
style: {
fontSize: '14px',
fontWeight: 600,
color: '#262626'
}
}, '编辑任务类型')
]),
h(Space, {
direction: 'vertical',
size: 8,
style: { width: '100%', marginBottom: '12px' }
}, [
...editableTaskTypes.map((item, index) =>
h('div', {
key: index,
style: {
display: 'flex',
alignItems: 'center',
padding: '12px',
background: 'white',
borderRadius: '6px',
border: '1px solid #e8e8e8',
transition: 'all 0.3s'
}
}, [
h(Tag, {
color: 'blue',
style: {
marginRight: '12px',
fontSize: '13px',
padding: '4px 12px'
}
}, getTaskTypeName(item.typeId)),
h('span', {
style: {
flex: 1,
fontSize: '13px',
color: '#8c8c8c'
}
}),
h('span', {
style: {
marginRight: '8px',
fontSize: '13px',
color: '#595959'
}
}, '数量:'),
h(InputNumber, {
value: item.quantity,
min: 1,
size: 'small',
style: { width: '80px', marginRight: '8px' },
onChange: (val: number) => {
item.quantity = val || 1
}
}),
h(Button, {
type: 'text',
danger: true,
size: 'small',
onClick: () => {
editableTaskTypes.splice(index, 1)
}
}, '删除')
])
)
]),
h(Space, { size: 8, style: { width: '100%' } }, [
h(Select, {
value: newTaskTypeId,
placeholder: '选择要添加的任务类型',
allowClear: true,
style: { flex: 1, minWidth: '200px' },
onChange: (val: number) => {
newTaskTypeId = val
},
options: demandTypes.value.map((type: any) => ({
value: type.id,
label: type.name
}))
}),
h(Button, {
type: 'primary',
size: 'middle',
style: { background: '#52c41a', borderColor: '#52c41a' },
onClick: () => {
if (newTaskTypeId && !editableTaskTypes.find(t => t.typeId === newTaskTypeId)) {
editableTaskTypes.push({ typeId: newTaskTypeId, quantity: 1 })
newTaskTypeId = null
} else if (newTaskTypeId) {
message.warning('该任务类型已添加')
} else {
message.warning('请先选择任务类型')
}
}
}, '+ 添加类型')
])
]),
// 选择接单方式
h('div', {
style: {
marginBottom: '24px'
}
}, [
h('div', {
style: {
marginBottom: '12px',
fontSize: '14px',
fontWeight: 500,
color: '#262626'
}
}, '选择任务接单方式'),
h(RadioGroup, {
value: assignType,
onChange: (e: any) => {
assignType = e.target.value
},
style: { width: '100%' }
}, [
h(Radio, {
value: 'auto',
style: {
display: 'block',
marginBottom: '12px',
padding: '10px 12px',
border: '1px solid #d9d9d9',
borderRadius: '6px',
background: assignType === 'auto' ? '#e6f7ff' : 'white',
transition: 'all 0.3s'
}
}, '🏢 发布到任务大厅(设计师自由抢单)'),
h(Radio, {
value: 'manual',
style: {
display: 'block',
padding: '10px 12px',
border: '1px solid #d9d9d9',
borderRadius: '6px',
background: assignType === 'manual' ? '#e6f7ff' : 'white',
transition: 'all 0.3s'
}
}, '👤 指定设计师接单')
])
]),
// 设计师列表(仅在指定接单时显示)
h('div', {
style: {
display: assignType === 'manual' ? 'block' : 'none'
}
}, [
h('div', {
style: {
marginBottom: '12px',
fontSize: '14px',
fontWeight: 500,
color: '#262626'
}
}, '选择设计师'),
h(Select, {
value: selectedDesignerId,
placeholder: '请选择要指派的设计师',
style: { width: '100%' },
size: 'large',
onChange: (val: number) => {
selectedDesignerId = val
},
options: designers.map(designer => {
const formatDeadline = (deadline: string) => {
if (!deadline) return ''
try {
const date = new Date(deadline)
const now = new Date()
const diff = date.getTime() - now.getTime()
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
if (diff < 0) return '已逾期'
if (days === 0) return `${hours}小时后`
return `${days}天${hours}小时后`
} catch {
return ''
}
}
const workloadColor = designer.inProgressCount > 5 ? '#ff4d4f' :
designer.inProgressCount > 3 ? '#faad14' : '#52c41a'
const workloadText = designer.inProgressCount === 0 ? '空闲' :
`进行中: ${designer.inProgressCount}个`
const deadlineText = designer.earliestDeadline ?
` | 最早完成: ${formatDeadline(designer.earliestDeadline)}` : ''
return {
value: designer.id,
label: h('div', {
style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '4px 0'
}
}, [
h('span', {
style: {
fontSize: '14px',
fontWeight: 500,
flex: 1
}
}, designer.realName),
h('span', {
style: {
fontSize: '12px',
color: workloadColor,
marginLeft: '12px'
}
}, `${workloadText}${deadlineText}`)
])
}
})
})
])
])
Modal.confirm({
title: '✅ 接单部门审核通过',
content: modalContent,
width: 650,
okText: '确认通过',
cancelText: '取消',
okButtonProps: {
size: 'large',
style: { minWidth: '100px' }
},
cancelButtonProps: {
size: 'large',
style: { minWidth: '100px' }
},
onOk: async () => {
try {
approvalLoading.value = true
// 检查是否需要指定设计师
if (assignType === 'manual' && !selectedDesignerId) {
message.error('请选择要指定的设计师')
return Promise.reject()
}
// 检查任务类型是否为空
if (editableTaskTypes.length === 0) {
message.error('至少需要一个任务类型')
return Promise.reject()
}
// 准备任务类型更新数据
const approvalData: TaskApprovalDTO = {
comment: assignType === 'manual' ?
`指定设计师接单` : '审核通过,发布到任务大厅',
assignedReceiverId: assignType === 'manual' && selectedDesignerId ? selectedDesignerId : undefined,
taskUpdates: {
taskTypes: editableTaskTypes.map(t => ({
taskTypeId: t.typeId,
quantity: t.quantity
}))
}
}
await taskApi.approveReceivingLevel(task.id!, approvalData)
if (assignType === 'manual') {
const designer = designers.find(d => d.id === selectedDesignerId)
message.success(`已指定设计师 ${designer?.realName} 接单`)
} else {
message.success('审核通过,任务已发布到大厅')
}
// 刷新列表和数量
loadPendingApprovalTasks()
loadAllTabCounts()
loadDashboardStats()
} catch (error: any) {
console.error('审核失败:', error)
message.error(error.msg || error.message || '审核失败')
return Promise.reject(error)
} finally {
approvalLoading.value = false
}
}
})
approveModalVisible.value = true
} catch (error: any) {
console.error('获取设计师列表失败:', error)
message.error('获取设计师列表失败')
}
}
// 确认审核通过
const handleApproveConfirm = async () => {
if (!approveTaskData.value) return
try {
approvalLoading.value = true
// 检查是否需要指定设计师
if (approveAssignType.value === 'manual' && !approveDesignerId.value) {
message.error('请选择要指定的设计师')
return
}
// 检查任务类型是否为空
if (approveTaskTypes.value.length === 0) {
message.error('至少需要一个任务类型')
return
}
const approvalData: TaskApprovalDTO = {
comment: approveAssignType.value === 'manual' ?
`指定设计师接单` : '审核通过,发布到任务大厅',
assignedReceiverId: approveAssignType.value === 'manual' ? approveDesignerId.value : undefined,
taskUpdates: {
taskTypes: approveTaskTypes.value.map(t => ({
taskTypeId: t.typeId,
quantity: t.quantity
}))
}
}
await taskApi.approveReceivingLevel(approveTaskData.value.id!, approvalData)
if (approveAssignType.value === 'manual') {
const designer = designersList.value.find(d => d.id === approveDesignerId.value)
message.success(`已指定设计师 ${designer?.realName} 接单`)
} else {
message.success('审核通过,任务已发布到大厅')
}
approveModalVisible.value = false
// 刷新列表和数量
loadPendingApprovalTasks()
loadAllTabCounts()
loadDashboardStats()
} catch (error: any) {
console.error('审核失败:', error)
message.error(error.msg || error.message || '审核失败')
} finally {
approvalLoading.value = false
}
}
// 两级审核:驳回任务
const handleRejectTwoLevelTask = async (task: Task) => {
let rejectReason = ''
@ -4602,5 +4496,119 @@ onUnmounted(() => {
}
}
}
/* 审批弹窗暗黑主题适配 */
:deep(.approve-modal) .ant-modal-content,
:deep(.approve-modal) .ant-modal-header {
background-color: #1f1f1f !important;
color: #fff !important;
border-bottom: 1px solid #303030;
}
:deep(.approve-modal) .ant-modal-title {
color: #fff !important;
}
:deep(.approve-modal) .ant-modal-close {
color: rgba(255, 255, 255, 0.45) !important;
&:hover {
color: #fff !important;
}
}
:deep(.approve-modal) .ant-radio-wrapper {
color: #fff !important;
transition: all 0.3s;
&:hover .ant-radio-inner {
border-color: #31f08b !important;
}
}
:deep(.approve-modal) .ant-radio-checked .ant-radio-inner {
border-color: #31f08b !important;
background-color: #31f08b !important;
}
:deep(.approve-modal) .ant-radio-checked .ant-radio-inner::after {
background-color: #fff !important;
}
/* 选中状态的 Radio 卡片边框颜色 */
:deep(.approve-modal) .assign-option.ant-radio-wrapper-checked {
border-color: #31f08b !important;
background-color: rgba(49, 240, 139, 0.1) !important;
}
/* 下拉框暗黑适配 */
:deep(.approve-modal) .ant-select-selector {
background-color: #141414 !important;
border-color: #303030 !important;
color: #fff !important;
}
:deep(.approve-modal) .ant-select:not(.ant-select-disabled):hover .ant-select-selector,
:deep(.approve-modal) .ant-select-focused .ant-select-selector {
border-color: #31f08b !important;
box-shadow: 0 0 0 2px rgba(49, 240, 139, 0.2) !important;
}
:deep(.approve-modal) .ant-select-arrow {
color: rgba(255, 255, 255, 0.45) !important;
}
:deep(.approve-modal) .ant-select-item-option-selected {
background-color: rgba(49, 240, 139, 0.1) !important;
color: #fff;
font-weight: 600;
}
:deep(.approve-modal) .ant-select-item-option-active {
background-color: rgba(255, 255, 255, 0.08) !important;
}
/* 输入框适配 */
:deep(.approve-modal) .ant-input-number {
background-color: #141414;
border-color: #303030;
color: #fff;
}
:deep(.approve-modal) .ant-input-number:hover,
:deep(.approve-modal) .ant-input-number-focused {
border-color: #31f08b;
}
:deep(.approve-modal) .ant-input-number-handler-wrap {
background-color: #262626;
border-left-color: #303030;
}
:deep(.approve-modal) .ant-input-number-handler {
border-top-color: #303030;
&:hover .ant-input-number-handler-up-inner,
&:hover .ant-input-number-handler-down-inner {
color: #31f08b;
}
}
/* 按钮适配 */
:deep(.approve-modal) .ant-btn-primary {
background-color: #31f08b;
border-color: #31f08b;
color: #000;
font-weight: 500;
&:hover {
background-color: #5cf2a3;
border-color: #5cf2a3;
}
}
:deep(.approve-modal) .ant-btn-text.ant-btn-danger:hover {
background-color: rgba(255, 77, 79, 0.1);
}
</style>