修改员工界面
This commit is contained in:
@@ -10,10 +10,21 @@
|
||||
|
||||
<div class="identity-badges">
|
||||
<el-space :size="8">
|
||||
<el-tag v-if="realStaffInfo" type="warning" effect="plain" round size="small">
|
||||
<el-icon><User /></el-icon> 牛马身份: {{ realStaffInfo.name }}
|
||||
<el-tag
|
||||
v-if="realStaffInfo"
|
||||
type="warning"
|
||||
effect="plain"
|
||||
round
|
||||
size="small"
|
||||
>
|
||||
<el-icon>
|
||||
<User />
|
||||
</el-icon>
|
||||
牛马身份: {{ realStaffInfo.name }}
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -21,29 +32,52 @@
|
||||
|
||||
<div class="header-action">
|
||||
<el-button type="danger" plain @click="handleLogout">
|
||||
<el-icon><SwitchButton /></el-icon> 退出登录
|
||||
<el-icon> <SwitchButton /> </el-icon> 退出登录
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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-icon><Notification /></el-icon> 待分配任务
|
||||
<el-icon>
|
||||
<Notification />
|
||||
</el-icon>
|
||||
待分配任务
|
||||
</el-radio-button>
|
||||
<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 label="已完成">
|
||||
<el-icon><CircleCheck /></el-icon> 我的已完成
|
||||
<el-icon>
|
||||
<CircleCheck />
|
||||
</el-icon>
|
||||
我的已完成
|
||||
</el-radio-button>
|
||||
<el-radio-button label="已完结">
|
||||
<el-icon><Checked /></el-icon> 我的已完结
|
||||
<el-icon>
|
||||
<Checked />
|
||||
</el-icon>
|
||||
我的已完结
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</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>
|
||||
<el-empty :image-size="120" :description="emptyText">
|
||||
<el-button type="primary" plain @click="fetchTickets">刷新列表</el-button>
|
||||
@@ -51,26 +85,55 @@
|
||||
</template>
|
||||
|
||||
<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">
|
||||
<template #default="scope">
|
||||
<div class="cell-content-card">
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</template>
|
||||
<div class="popover-content">
|
||||
<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">
|
||||
<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>
|
||||
</el-popover>
|
||||
@@ -78,19 +141,30 @@
|
||||
</template>
|
||||
</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">
|
||||
<div class="cell-content-card">
|
||||
<div class="data-display-area" style="width: 100%; padding: 0 10px;">
|
||||
<el-progress
|
||||
:percentage="Number(scope.row.progress || 0)"
|
||||
<div class="data-display-area" style="width: 100%; padding: 0 10px">
|
||||
<el-progress
|
||||
:percentage="Number(scope.row.progress || 0)"
|
||||
:status="Number(scope.row.progress) === 100 ? 'success' : ''"
|
||||
:stroke-width="12"
|
||||
striped
|
||||
striped-flow
|
||||
/>
|
||||
</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>
|
||||
<el-button type="primary" size="small" link class="edit-link-btn">
|
||||
[ 更新进度 ]
|
||||
@@ -98,15 +172,24 @@
|
||||
</template>
|
||||
<div class="popover-content">
|
||||
<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="30">30</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="100">100</el-radio-button>
|
||||
<el-radio-button :label="99">99</el-radio-button>
|
||||
</el-radio-group>
|
||||
<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>
|
||||
</el-popover>
|
||||
@@ -115,25 +198,48 @@
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="operator" label="运营" width="120" align="center" />
|
||||
|
||||
|
||||
<el-table-column label="备注" min-width="150" align="center">
|
||||
<template #default="scope">
|
||||
<div class="cell-content-card">
|
||||
<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>
|
||||
</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>
|
||||
<el-button type="warning" size="small" link class="edit-link-btn">
|
||||
{{ scope.row.remark ? '[ 修改备注 ]' : '[ 输入备注 ]' }}
|
||||
{{ scope.row.remark ? "[ 修改备注 ]" : "[ 输入备注 ]" }}
|
||||
</el-button>
|
||||
</template>
|
||||
<div class="popover-content">
|
||||
<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">
|
||||
<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>
|
||||
</el-popover>
|
||||
@@ -141,18 +247,46 @@
|
||||
</template>
|
||||
</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">
|
||||
<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>
|
||||
</template>
|
||||
</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">
|
||||
<el-button v-if="scope.row.is_complete === '未分配'" type="primary" size="small" :disabled="!realStaffInfo" @click="handleAccept(scope.row)">立即接单</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>
|
||||
<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>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -173,138 +307,227 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { User, SwitchButton, Timer, Notification, CircleCheck, Checked, Loading } from '@element-plus/icons-vue'
|
||||
import { ticketApi } from '@/api/ticket'
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import {
|
||||
User,
|
||||
SwitchButton,
|
||||
Timer,
|
||||
Notification,
|
||||
CircleCheck,
|
||||
Checked,
|
||||
Loading,
|
||||
} from "@element-plus/icons-vue";
|
||||
import { ticketApi } from "@/api/ticket";
|
||||
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const allTickets = ref([])
|
||||
const currentStatus = ref('未分配')
|
||||
const realStaffInfo = ref(null)
|
||||
const editValue = ref('')
|
||||
const router = useRouter();
|
||||
const loading = ref(false);
|
||||
const allTickets = ref([]);
|
||||
const currentStatus = ref("未分配");
|
||||
const realStaffInfo = ref(null);
|
||||
const editValue = ref("");
|
||||
|
||||
const currentPage = ref(1)
|
||||
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')}`;
|
||||
}
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(15);
|
||||
|
||||
// js计算时间
|
||||
const welcomeMessage = computed(() => {
|
||||
const hour = new Date().getHours()
|
||||
if (hour < 6) return '凌晨好'; if (hour < 9) return '早上好'; if (hour < 12) return '上午好';
|
||||
if (hour < 14) return '中午好'; if (hour < 18) return '下午好'; return '晚上好'
|
||||
})
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 6) return "凌晨好";
|
||||
if (hour < 9) return "早上好";
|
||||
if (hour < 12) return "上午好";
|
||||
if (hour < 14) return "中午好";
|
||||
if (hour < 18) return "下午好";
|
||||
return "晚上好";
|
||||
});
|
||||
|
||||
// 获取当前用户信息
|
||||
const currentUser = computed(() => {
|
||||
const info = localStorage.getItem('user_info') || sessionStorage.getItem('user_info')
|
||||
return info ? JSON.parse(info) : { id: null, username: '未知' }
|
||||
})
|
||||
const info = localStorage.getItem("user_info") || sessionStorage.getItem("user_info");
|
||||
return info ? JSON.parse(info) : { id: null, username: "未知" };
|
||||
});
|
||||
|
||||
// 获取工单信息
|
||||
const fetchTickets = async () => {
|
||||
loading.value = true
|
||||
loading.value = true;
|
||||
// 优化:不再获取所有工单,而是根据当前视图的状态和用户ID进行筛选
|
||||
try {
|
||||
const res = await ticketApi.getTickets({ page: 1, limit: 1000 })
|
||||
// console.log(res)
|
||||
if (res.code === 200 || res.code === true) allTickets.value = res.data.data
|
||||
} catch (e) { ElMessage.error('获取列表失败') }
|
||||
finally { loading.value = false }
|
||||
}
|
||||
const statusMap = {
|
||||
未分配: 0,
|
||||
进行中: 1,
|
||||
已完成: 2,
|
||||
已完结: 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(() => {
|
||||
return allTickets.value.filter(ticket => {
|
||||
if (ticket.is_complete !== currentStatus.value) return false
|
||||
if (currentStatus.value !== '未分配') return String(ticket.staff_id) === String(realStaffInfo.value?.id)
|
||||
return true
|
||||
})
|
||||
})
|
||||
// 优化:由于 fetchTickets 已经从后端获取了经过筛选的数据,
|
||||
// allTickets 本身就是过滤后的列表,无需再在客户端进行过滤。
|
||||
return allTickets.value;
|
||||
});
|
||||
|
||||
const paginatedTickets = computed(() => {
|
||||
const start = (currentPage.value - 1) * pageSize.value
|
||||
const end = start + pageSize.value
|
||||
return filteredTickets.value.slice(start, end)
|
||||
})
|
||||
const start = (currentPage.value - 1) * pageSize.value;
|
||||
const end = start + pageSize.value;
|
||||
return filteredTickets.value.slice(start, end);
|
||||
});
|
||||
|
||||
const handleSizeChange = (val) => { pageSize.value = val; currentPage.value = 1; }
|
||||
const handleCurrentChange = (val) => { currentPage.value = val; }
|
||||
const handleStatusChange = () => { currentPage.value = 1; fetchTickets(); }
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
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) => {
|
||||
try {
|
||||
const payload = {
|
||||
datanum: field === 'datanum' ? String(editValue.value) : String(row.datanum || ''),
|
||||
remark: field === 'remark' ? editValue.value : (row.remark || ''),
|
||||
progress: field === 'progress' ? Number(editValue.value) : Number(row.progress || 0)
|
||||
}
|
||||
const res = await ticketApi.updateTicket(row.id, payload)
|
||||
datanum: field === "datanum" ? String(editValue.value) : String(row.datanum || ""),
|
||||
remark: field === "remark" ? editValue.value : row.remark || "",
|
||||
progress:
|
||||
field === "progress" ? Number(editValue.value) : Number(row.progress || 0),
|
||||
};
|
||||
const res = await ticketApi.updateTicket(row.id, payload);
|
||||
if (res.code === 200 || res.code === true) {
|
||||
row[field] = editValue.value;
|
||||
ElMessage.success('更新成功');
|
||||
document.body.click()
|
||||
row[field] = editValue.value;
|
||||
ElMessage.success("更新成功");
|
||||
document.body.click();
|
||||
}
|
||||
} catch (e) { ElMessage.error('操作失败') }
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error("操作失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 点击接受工单
|
||||
const handleAccept = (row) => {
|
||||
ElMessageBox.confirm(`确认接手工单 #${row.id}?`).then(async () => {
|
||||
try {
|
||||
const res = await ticketApi.updateTicket(row.id, { staff_id: realStaffInfo.value.id, is_complete: '进行中', progress: 0 })
|
||||
if (res.code === 200 || res.code === true) { ElMessage.success('接单成功'); fetchTickets() }
|
||||
} catch (e) { ElMessage.error('操作失败') }
|
||||
})
|
||||
}
|
||||
const res = await ticketApi.updateTicket(row.id, {
|
||||
staff_id: realStaffInfo.value.id,
|
||||
is_complete: "进行中",
|
||||
progress: 0,
|
||||
});
|
||||
if (res.code === 200 || res.code === true) {
|
||||
ElMessage.success("接单成功");
|
||||
fetchTickets();
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error("操作失败");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 点击完成
|
||||
const handleFinish = (row) => {
|
||||
ElMessageBox.confirm('确认已完成任务?').then(async () => {
|
||||
ElMessageBox.confirm("确认已完成任务?").then(async () => {
|
||||
try {
|
||||
const currentTimestamp = Math.floor(Date.now() / 1000);
|
||||
// 关键修改:在提交时增加 progress: 100
|
||||
const res = await ticketApi.updateTicket(row.id, {
|
||||
is_complete: '已完成',
|
||||
lock_time: String(currentTimestamp),
|
||||
progress: 100 // 默认改为100
|
||||
})
|
||||
|
||||
if (res.code === 200 || res.code === true) {
|
||||
ElMessage.success('任务已完成,进度已更新为100%');
|
||||
fetchTickets()
|
||||
const res = await ticketApi.updateTicket(row.id, {
|
||||
is_complete: "已完成",
|
||||
lock_time: String(currentTimestamp),
|
||||
progress: 100, // 默认改为100
|
||||
});
|
||||
|
||||
if (res.code === 200 || res.code === true) {
|
||||
ElMessage.success("任务已完成,进度已更新为100%");
|
||||
fetchTickets();
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('操作失败')
|
||||
} catch (e) {
|
||||
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 = () => {
|
||||
ElMessageBox.confirm('确定退出当前账号?', '提示', { type: 'warning' }).then(() => {
|
||||
localStorage.clear(); sessionStorage.clear(); router.push('/login')
|
||||
})
|
||||
}
|
||||
ElMessageBox.confirm("确定退出当前账号?", "提示", { type: "warning" }).then(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
router.push("/login");
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await ticketApi.getStaffs()
|
||||
const res = await ticketApi.getStaffs();
|
||||
if (res.code === 200 || res.code === true) {
|
||||
const match = (res.data || []).find(s => s.name === currentUser.value.username)
|
||||
if (match) realStaffInfo.value = match
|
||||
const match = (res.data || []).find((s) => s.name === currentUser.value.username);
|
||||
if (match) realStaffInfo.value = match;
|
||||
}
|
||||
} catch (e) {}
|
||||
fetchTickets()
|
||||
})
|
||||
await fetchTickets();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -318,7 +541,8 @@ onMounted(async () => {
|
||||
.welcome-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px; /* 这里统一控制欢迎语、副标题、标签之间的垂直间距 */
|
||||
gap: 8px;
|
||||
/* 这里统一控制欢迎语、副标题、标签之间的垂直间距 */
|
||||
}
|
||||
|
||||
/* 清除标题默认的垂直 margin,防止撑开间距 */
|
||||
@@ -331,14 +555,16 @@ onMounted(async () => {
|
||||
|
||||
/* 副标题紧贴标题 */
|
||||
.sub-text {
|
||||
margin: 4px 0 0 0; /* 仅顶部保留微小间距 */
|
||||
margin: 4px 0 0 0;
|
||||
/* 仅顶部保留微小间距 */
|
||||
color: #8c8c8c;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 标签组位置微调 */
|
||||
.identity-badges {
|
||||
margin-top: 4px; /* 让标签组靠近上方的文字 */
|
||||
margin-top: 4px;
|
||||
/* 让标签组靠近上方的文字 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -347,46 +573,167 @@ onMounted(async () => {
|
||||
:deep(.el-tag) {
|
||||
display: inline-flex;
|
||||
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 { 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); }
|
||||
.greeting { margin: 0 0 8px 0; color: #1a1a1a; font-size: 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); }
|
||||
.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; }
|
||||
.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);
|
||||
}
|
||||
|
||||
.custom-table { border-radius: 12px; overflow: hidden; 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; }
|
||||
.greeting {
|
||||
margin: 0 0 8px 0;
|
||||
color: #1a1a1a;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.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; }
|
||||
.progress-radio-group :deep(.el-radio-button__inner) { padding: 8px 10px !important; }
|
||||
.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;
|
||||
}
|
||||
|
||||
: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; }
|
||||
.custom-table {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
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. 强行修正进度条内部容器的对齐方式 */
|
||||
:deep(.el-progress-bar__innerText) {
|
||||
display: flex !important;
|
||||
align-items: center !important; /* 垂直居中 */
|
||||
justify-content: center !important; /* 水平居中 */
|
||||
align-items: center !important;
|
||||
/* 垂直居中 */
|
||||
justify-content: center !important;
|
||||
/* 水平居中 */
|
||||
height: 100% !important;
|
||||
line-height: 1 !important;
|
||||
/* 移除默认的基线对齐 */
|
||||
vertical-align: middle !important;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
/* 2. 针对 100% 时出现的那个 svg 图标进行位移修正 */
|
||||
@@ -395,7 +742,7 @@ onMounted(async () => {
|
||||
margin: 0 !important;
|
||||
/* 关键:利用相对定位向上平移 1px,抵消视觉上的下沉 */
|
||||
position: relative !important;
|
||||
top: -1px !important;
|
||||
top: -1px !important;
|
||||
/* 稍微调整图标大小,让它在 10px 的进度条里更协调 */
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
@@ -406,4 +753,4 @@ onMounted(async () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user