wip
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"tokens": {},
|
||||||
|
"components": {}
|
||||||
|
}
|
||||||
54
src/components/common/SearchInput.vue
Normal file
54
src/components/common/SearchInput.vue
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<el-input
|
||||||
|
v-model="keyword"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
clearable
|
||||||
|
class="search-input"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
@clear="handleSearch"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<el-icon><Search /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { Search } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: { type: String, default: '' },
|
||||||
|
placeholder: { type: String, default: '搜索网站、备注...' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'search'])
|
||||||
|
|
||||||
|
const keyword = ref(props.modelValue)
|
||||||
|
|
||||||
|
watch(() => props.modelValue, (val) => {
|
||||||
|
keyword.value = val
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
emit('update:modelValue', keyword.value)
|
||||||
|
emit('search', keyword.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search-input {
|
||||||
|
width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input :deep(.el-input__wrapper) {
|
||||||
|
border-radius: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1400px) {
|
||||||
|
.search-input { width: 260px; }
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.search-input { width: 100%; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -34,14 +34,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="关键词" class="search-keyword">
|
<el-form-item label="关键词" class="search-keyword">
|
||||||
<el-input v-model="localFilters.search" placeholder="搜索网站、备注" clearable @keyup.enter="handleSearch"
|
<SearchInput v-model="localFilters.search" @search="handleSearch" />
|
||||||
class="wide-search-input">
|
|
||||||
<template #prefix>
|
|
||||||
<el-icon>
|
|
||||||
<Search />
|
|
||||||
</el-icon>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,6 +63,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, watch, computed } from "vue";
|
import { reactive, watch, computed } from "vue";
|
||||||
import { Search, Sort } from "@element-plus/icons-vue";
|
import { Search, Sort } from "@element-plus/icons-vue";
|
||||||
|
import SearchInput from "@/components/common/SearchInput.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
filters: { type: Object, required: true },
|
filters: { type: Object, required: true },
|
||||||
@@ -159,8 +153,6 @@ const setSort = (order) => {
|
|||||||
:deep(.el-form-item__label) { color: var(--text-secondary); font-weight: 500; }
|
:deep(.el-form-item__label) { color: var(--text-secondary); font-weight: 500; }
|
||||||
|
|
||||||
.wide-select { width: 180px; }
|
.wide-select { width: 180px; }
|
||||||
.wide-search-input { width: 320px; }
|
|
||||||
|
|
||||||
.filter-main :deep(.el-input__wrapper),
|
.filter-main :deep(.el-input__wrapper),
|
||||||
.filter-main :deep(.el-button) { border-radius: 20px !important; }
|
.filter-main :deep(.el-button) { border-radius: 20px !important; }
|
||||||
:deep(.el-select-dropdown) { border-radius: 12px !important; overflow: hidden; }
|
:deep(.el-select-dropdown) { border-radius: 12px !important; overflow: hidden; }
|
||||||
@@ -175,11 +167,10 @@ const setSort = (order) => {
|
|||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1400px) { .wide-search-input { width: 260px; } }
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.search-container { flex-direction: column; }
|
.search-container { flex-direction: column; }
|
||||||
.input-group-row { flex-direction: column; align-items: flex-start; }
|
.input-group-row { flex-direction: column; align-items: flex-start; }
|
||||||
.divider-wrapper { display: none; }
|
.divider-wrapper { display: none; }
|
||||||
.wide-select, .wide-search-input { width: 100%; }
|
.wide-select { width: 100%; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -62,6 +62,12 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="任务进度">
|
||||||
|
<div class="progress-input-wrap">
|
||||||
|
<el-slider v-model="form.progress" :min="0" :max="100" :step="5" show-input />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="填写注意事项" resize="none" />
|
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="填写注意事项" resize="none" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -97,6 +103,7 @@ const props = defineProps({
|
|||||||
const dialogVisible = ref(false);
|
const dialogVisible = ref(false);
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const submitting = ref(false);
|
const submitting = ref(false);
|
||||||
|
const isInitializing = ref(false);
|
||||||
|
|
||||||
// 核心表单对象:customer_id 已包含在内
|
// 核心表单对象:customer_id 已包含在内
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -106,6 +113,7 @@ const form = reactive({
|
|||||||
operator_id: null,
|
operator_id: null,
|
||||||
user_id: null,
|
user_id: null,
|
||||||
status: 0,
|
status: 0,
|
||||||
|
progress: 0,
|
||||||
remark: "",
|
remark: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,6 +131,17 @@ const handleCustomerChange = () => {
|
|||||||
form.operator_id = null;
|
form.operator_id = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 状态变为已完成(2)或已完结(3)时,自动将进度设为100
|
||||||
|
watch(
|
||||||
|
() => form.status,
|
||||||
|
(val) => {
|
||||||
|
if (isInitializing.value) return;
|
||||||
|
if (val === 2 || val === 3) {
|
||||||
|
form.progress = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// --- 监听弹窗与回显 ---
|
// --- 监听弹窗与回显 ---
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
@@ -130,6 +149,7 @@ watch(
|
|||||||
dialogVisible.value = val;
|
dialogVisible.value = val;
|
||||||
if (val) { // 只在弹窗打开时候,执行初始化
|
if (val) { // 只在弹窗打开时候,执行初始化
|
||||||
nextTick(() => { // 等待DOM更新完成后再操作表单数据
|
nextTick(() => { // 等待DOM更新完成后再操作表单数据
|
||||||
|
isInitializing.value = true;
|
||||||
resetForm(); // 先重置
|
resetForm(); // 先重置
|
||||||
if (props.isEditing && props.formData) {
|
if (props.isEditing && props.formData) {
|
||||||
const d = props.formData;
|
const d = props.formData;
|
||||||
@@ -167,6 +187,7 @@ watch(
|
|||||||
if (u) form.user_id = u.id;
|
if (u) form.user_id = u.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isInitializing.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,6 +208,7 @@ const resetForm = () => {
|
|||||||
operator_id: null,
|
operator_id: null,
|
||||||
user_id: null,
|
user_id: null,
|
||||||
status: 0,
|
status: 0,
|
||||||
|
progress: 0,
|
||||||
remark: "",
|
remark: "",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -261,6 +283,10 @@ const handleSubmit = async () => {
|
|||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-input-wrap {
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-form-item__label) {
|
:deep(.el-form-item__label) {
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
<el-table
|
<el-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:data="tickets"
|
:data="tickets"
|
||||||
|
row-key="id"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
header-cell-class-name="table-header"
|
header-cell-class-name="table-header"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@selection-change="$emit('selection-change', $event)"
|
@select="onSelect"
|
||||||
|
@select-all="onSelectAll"
|
||||||
>
|
>
|
||||||
<el-table-column type="selection" width="50" />
|
<el-table-column type="selection" width="50" :reserve-selection="true" />
|
||||||
<el-table-column label="网站地址" min-width="180">
|
<el-table-column label="网站地址" min-width="180">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link
|
<el-link
|
||||||
@@ -147,21 +149,43 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref, reactive } from "vue";
|
||||||
import { formatUrl, formatDateTime } from "@/utils/format";
|
import { formatUrl, formatDateTime } from "@/utils/format";
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
tickets: { type: Array, default: () => [] },
|
tickets: { type: Array, default: () => [] },
|
||||||
loading: { type: Boolean, default: false },
|
loading: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(["edit", "delete", "selection-change"]);
|
const emit = defineEmits(["edit", "delete", "selection-change"]);
|
||||||
|
|
||||||
const tableRef = ref(null);
|
const tableRef = ref(null);
|
||||||
|
const selectedMap = reactive(new Map());
|
||||||
|
|
||||||
|
const emitSelection = () => {
|
||||||
|
emit("selection-change", Array.from(selectedMap.values()));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (selection, row) => {
|
||||||
|
if (selection.some((r) => r.id === row.id)) {
|
||||||
|
selectedMap.set(row.id, row);
|
||||||
|
} else {
|
||||||
|
selectedMap.delete(row.id);
|
||||||
|
}
|
||||||
|
emitSelection();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelectAll = (selection) => {
|
||||||
|
props.tickets.forEach((t) => selectedMap.delete(t.id));
|
||||||
|
selection.forEach((row) => selectedMap.set(row.id, row));
|
||||||
|
emitSelection();
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
tableRef.value?.clearSelection();
|
tableRef.value?.clearSelection();
|
||||||
|
selectedMap.clear();
|
||||||
|
emit("selection-change", []);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ function createService(basePath) {
|
|||||||
service.interceptors.response.use(
|
service.interceptors.response.use(
|
||||||
response => response.data,
|
response => response.data,
|
||||||
|
|
||||||
|
|
||||||
error => {
|
error => {
|
||||||
const resData = error.response?.data
|
const resData = error.response?.data
|
||||||
const status = error.response?.status
|
const status = error.response?.status
|
||||||
|
|||||||
@@ -7,36 +7,23 @@
|
|||||||
<p class="desc">登录以继续使用系统</p>
|
<p class="desc">登录以继续使用系统</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-form
|
<el-form ref="formRef" :model="form" :rules="rules" class="login-form" @keyup.enter="handleLogin">
|
||||||
ref="formRef"
|
|
||||||
:model="form"
|
|
||||||
:rules="rules"
|
|
||||||
class="login-form"
|
|
||||||
@keyup.enter="handleLogin"
|
|
||||||
>
|
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
<el-input
|
<el-input v-model="form.username" placeholder="员工编号" size="large" clearable>
|
||||||
v-model="form.username"
|
|
||||||
placeholder="员工编号"
|
|
||||||
size="large"
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<el-icon><User /></el-icon>
|
<el-icon>
|
||||||
|
<User />
|
||||||
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="password">
|
<el-form-item prop="password">
|
||||||
<el-input
|
<el-input v-model="form.password" type="password" placeholder="访问密钥" size="large" show-password>
|
||||||
v-model="form.password"
|
|
||||||
type="password"
|
|
||||||
placeholder="访问密钥"
|
|
||||||
size="large"
|
|
||||||
show-password
|
|
||||||
>
|
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<el-icon><Lock /></el-icon>
|
<el-icon>
|
||||||
|
<Lock />
|
||||||
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -45,13 +32,7 @@
|
|||||||
<el-checkbox v-model="rememberMe">保持登录</el-checkbox>
|
<el-checkbox v-model="rememberMe">保持登录</el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-button
|
<el-button type="primary" size="large" class="submit-btn" :loading="loading" @click="handleLogin">
|
||||||
type="primary"
|
|
||||||
size="large"
|
|
||||||
class="submit-btn"
|
|
||||||
:loading="loading"
|
|
||||||
@click="handleLogin"
|
|
||||||
>
|
|
||||||
登 录
|
登 录
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
<div class="batch-bar" v-if="selectedTickets.length">
|
<div class="batch-bar" v-if="selectedTickets.length">
|
||||||
<span class="batch-info">已选 <strong>{{ selectedTickets.length }}</strong> 个工单</span>
|
<span class="batch-info">已选 <strong>{{ selectedTickets.length }}</strong> 个工单</span>
|
||||||
<el-button type="warning" @click="handleBatchFinish">一键已完结</el-button>
|
<el-button type="warning" @click="handleBatchFinish">一键已完结</el-button>
|
||||||
|
<el-button type="primary" @click="handleBatchExport">一键导出</el-button>
|
||||||
<el-button link type="info" @click="ticketListRef.clearSelection()">取消选择</el-button>
|
<el-button link type="info" @click="ticketListRef.clearSelection()">取消选择</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -89,6 +90,7 @@ import {
|
|||||||
CircleCheck,
|
CircleCheck,
|
||||||
Checked,
|
Checked,
|
||||||
} from "@element-plus/icons-vue";
|
} from "@element-plus/icons-vue";
|
||||||
|
import * as XLSX from "xlsx";
|
||||||
|
|
||||||
// API 引入
|
// API 引入
|
||||||
import { ticketApi } from "@/api/ticket";
|
import { ticketApi } from "@/api/ticket";
|
||||||
@@ -272,6 +274,7 @@ const handleAdd = () => {
|
|||||||
operator_id: null,
|
operator_id: null,
|
||||||
user_id: null,
|
user_id: null,
|
||||||
status: 0,
|
status: 0,
|
||||||
|
progress: 0,
|
||||||
remark: "",
|
remark: "",
|
||||||
};
|
};
|
||||||
showAddDialog.value = true;
|
showAddDialog.value = true;
|
||||||
@@ -356,6 +359,32 @@ const handleBatchFinish = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleBatchExport = () => {
|
||||||
|
const data = selectedTickets.value.map((item) => ({
|
||||||
|
域名: item.website,
|
||||||
|
数据量: item.datanum || 0,
|
||||||
|
运营人员: item.operator_name,
|
||||||
|
采集人员: item.username,
|
||||||
|
备注: item.remark,
|
||||||
|
开始时间: item.create_time,
|
||||||
|
结束时间: item.lock_time,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const worksheet = XLSX.utils.json_to_sheet(data);
|
||||||
|
const keys = Object.keys(data[0] || {});
|
||||||
|
worksheet["!cols"] = keys.map((key) => ({
|
||||||
|
wch: Math.max(
|
||||||
|
key.replace(/[^\x00-\xff]/g, "00").length,
|
||||||
|
...data.map((r) => String(r[key] ?? "").replace(/[^\x00-\xff]/g, "00").length)
|
||||||
|
) + 3,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const workbook = XLSX.utils.book_new();
|
||||||
|
XLSX.utils.book_append_sheet(workbook, worksheet, "工单导出");
|
||||||
|
XLSX.writeFile(workbook, `工单导出_${new Date().toISOString().slice(0, 10)}.xlsx`);
|
||||||
|
ElMessage.success(`已导出 ${data.length} 条工单`);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSizeChange = (val) => {
|
const handleSizeChange = (val) => {
|
||||||
pagination.limit = val;
|
pagination.limit = val;
|
||||||
loadTickets();
|
loadTickets();
|
||||||
|
|||||||
@@ -1,48 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="user-view-container">
|
<div class="user-view-container">
|
||||||
|
|
||||||
|
<!-- 顶层框 -->
|
||||||
<div class="user-header-card">
|
<div class="user-header-card">
|
||||||
<div class="header-main">
|
<div class="header-main">
|
||||||
<div class="welcome-container">
|
<div class="welcome-container">
|
||||||
<div class="welcome-section">
|
<div class="welcome-section">
|
||||||
<h2 class="greeting">{{ welcomeMessage }},{{ currentUser.username }}</h2>
|
<h2 class="greeting">{{ welcomeMessage }},{{ currentUser.username }}</h2>
|
||||||
<p class="sub-text">欢迎回到你的垃圾堆,开启牛马的一天。</p>
|
<p class="sub-text">欢迎回到你的工作堆,开启牛马的一天。</p>
|
||||||
</div>
|
|
||||||
|
|
||||||
<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.username }}
|
|
||||||
</el-tag>
|
|
||||||
<el-tag v-else type="danger" round size="small"
|
|
||||||
>未录入员工表,无权接单</el-tag
|
|
||||||
>
|
|
||||||
</el-space>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-action">
|
<div class="header-action">
|
||||||
<el-button type="danger" plain @click="handleLogout">
|
<el-button type="danger" plain @click="handleLogout">
|
||||||
<el-icon> <SwitchButton /> </el-icon> 退出登录
|
<el-icon>
|
||||||
|
<SwitchButton />
|
||||||
|
</el-icon> 退出登录
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 状态标签 -->
|
||||||
|
<div class="tabs-toolbar-row">
|
||||||
<div class="status-tabs-wrapper">
|
<div class="status-tabs-wrapper">
|
||||||
<el-radio-group
|
<el-radio-group v-model="currentStatus" @change="handleStatusChange" class="custom-radio-group">
|
||||||
v-model="currentStatus"
|
|
||||||
@change="handleStatusChange"
|
|
||||||
class="custom-radio-group"
|
|
||||||
>
|
|
||||||
<el-radio-button label="未分配">
|
<el-radio-button label="未分配">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<Notification />
|
<Notification />
|
||||||
@@ -51,8 +33,7 @@
|
|||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
<el-radio-button label="进行中">
|
<el-radio-button label="进行中">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<Loading v-if="loading" />
|
<Timer />
|
||||||
<Timer v-else />
|
|
||||||
</el-icon>
|
</el-icon>
|
||||||
我的进行中
|
我的进行中
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
@@ -71,12 +52,17 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索框 -->
|
||||||
|
<div class="search-row">
|
||||||
|
<SearchInput v-model="searchKeyword" placeholder="搜索网站" @search="handleSearch" />
|
||||||
|
<el-button v-if="searchKeyword" :icon="Close" circle size="small" @click="handleClearSearch" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-card">
|
<div class="table-card">
|
||||||
<el-table
|
|
||||||
:data="paginatedTickets"
|
<!-- 表格 -->
|
||||||
v-loading="loading"
|
<el-table :data="paginatedTickets" v-loading="loading" class="custom-table">
|
||||||
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>
|
||||||
@@ -86,12 +72,7 @@
|
|||||||
<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">
|
<el-table-column prop="website" label="网站名称" min-width="150">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link
|
<el-link type="primary" :href="formatUrl(row.website)" target="_blank" rel="noopener noreferrer">
|
||||||
type="primary"
|
|
||||||
:href="formatUrl(row.website)"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{{ row.website }}
|
{{ row.website }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
@@ -101,20 +82,12 @@
|
|||||||
<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
|
<span v-if="scope.row.datanum && scope.row.datanum !== '0'" class="display-val">{{ scope.row.datanum
|
||||||
v-if="scope.row.datanum && scope.row.datanum !== '0'"
|
}}</span>
|
||||||
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
|
<el-popover v-if="currentStatus !== '未分配'" placement="top" :width="200" trigger="click"
|
||||||
v-if="currentStatus !== '未分配'"
|
@show="prepareEdit(scope.row, 'datanum')">
|
||||||
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">
|
||||||
{{
|
{{
|
||||||
@@ -126,19 +99,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<div class="popover-content">
|
<div class="popover-content">
|
||||||
<div class="pop-header">录入站点数据量</div>
|
<div class="pop-header">录入站点数据量</div>
|
||||||
<el-input
|
<el-input v-model="editValue" placeholder="请输入数值" size="small" clearable />
|
||||||
v-model="editValue"
|
|
||||||
placeholder="请输入数值"
|
|
||||||
size="small"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
<div class="pop-footer">
|
<div class="pop-footer">
|
||||||
<el-button
|
<el-button type="primary" size="small" @click="submitEdit(scope.row, 'datanum')">确认提交</el-button>
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="submitEdit(scope.row, 'datanum')"
|
|
||||||
>确认提交</el-button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
@@ -146,30 +109,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column v-if="currentStatus === '进行中'" label="任务进度" width="150" align="center">
|
||||||
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' : ''" :stroke-width="12" striped
|
||||||
:status="Number(scope.row.progress) === 100 ? 'success' : ''"
|
striped-flow />
|
||||||
:stroke-width="12"
|
|
||||||
striped
|
|
||||||
striped-flow
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<el-popover
|
<el-popover v-if="currentStatus === '进行中'" placement="top" :width="240" trigger="click"
|
||||||
v-if="currentStatus === '进行中'"
|
@show="prepareEdit(scope.row, 'progress')">
|
||||||
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">
|
||||||
[ 更新进度 ]
|
[ 更新进度 ]
|
||||||
@@ -177,11 +126,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<div class="popover-content">
|
<div class="popover-content">
|
||||||
<div class="pop-header">调整当前进度 (%)</div>
|
<div class="pop-header">调整当前进度 (%)</div>
|
||||||
<el-radio-group
|
<el-radio-group v-model="editValue" size="small" class="progress-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>
|
||||||
@@ -189,12 +134,7 @@
|
|||||||
<el-radio-button :label="99">99</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
|
<el-button type="primary" size="small" @click="submitEdit(scope.row, 'progress')">确定</el-button>
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="submitEdit(scope.row, 'progress')"
|
|
||||||
>确定</el-button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
@@ -208,22 +148,13 @@
|
|||||||
<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
|
<div v-if="scope.row.remark" class="display-remark" :title="scope.row.remark">
|
||||||
v-if="scope.row.remark"
|
|
||||||
class="display-remark"
|
|
||||||
:title="scope.row.remark"
|
|
||||||
>
|
|
||||||
{{ scope.row.remark }}
|
{{ scope.row.remark }}
|
||||||
</div>
|
</div>
|
||||||
<span v-else class="no-data-placeholder">-</span>
|
<span v-else class="no-data-placeholder">-</span>
|
||||||
</div>
|
</div>
|
||||||
<el-popover
|
<el-popover v-if="currentStatus !== '未分配'" placement="top" :width="250" trigger="click"
|
||||||
v-if="currentStatus !== '未分配'"
|
@show="prepareEdit(scope.row, 'remark')">
|
||||||
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 ? "[ 修改备注 ]" : "[ 输入备注 ]" }}
|
||||||
@@ -231,20 +162,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<div class="popover-content">
|
<div class="popover-content">
|
||||||
<div class="pop-header">录入工单备注</div>
|
<div class="pop-header">录入工单备注</div>
|
||||||
<el-input
|
<el-input v-model="editValue" type="textarea" :rows="3" placeholder="请输入备注" size="small" />
|
||||||
v-model="editValue"
|
|
||||||
type="textarea"
|
|
||||||
:rows="3"
|
|
||||||
placeholder="请输入备注"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
<div class="pop-footer">
|
<div class="pop-footer">
|
||||||
<el-button
|
<el-button type="primary" size="small" @click="submitEdit(scope.row, 'remark')">提交保存</el-button>
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="submitEdit(scope.row, 'remark')"
|
|
||||||
>提交保存</el-button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
@@ -252,11 +172,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column :label="['已完成', '已完结'].includes(currentStatus) ? '锁定日期' : '创建日期'" width="140" align="center">
|
||||||
:label="['已完成', '已完结'].includes(currentStatus) ? '锁定日期' : '创建日期'"
|
|
||||||
width="140"
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span class="pure-date">
|
<span class="pure-date">
|
||||||
{{
|
{{
|
||||||
@@ -272,42 +188,21 @@
|
|||||||
|
|
||||||
<el-table-column label="操作" width="190" fixed="right" align="center">
|
<el-table-column label="操作" width="190" fixed="right" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button v-if="Number(scope.row.status) === 0" type="primary" size="small" :disabled="!realStaffInfo"
|
||||||
v-if="Number(scope.row.status) === 0"
|
@click="handleAccept(scope.row)">立即接单</el-button>
|
||||||
type="primary"
|
<template v-if="Number(scope.row.status) === 1 && realStaffInfo">
|
||||||
size="small"
|
<el-button type="success" size="small" @click="handleFinish(scope.row)">完成任务</el-button>
|
||||||
:disabled="!realStaffInfo"
|
<el-button type="warning" plain size="small" @click="handleCancel(scope.row)">取消接单</el-button>
|
||||||
@click="handleAccept(scope.row)"
|
|
||||||
>立即接单</el-button
|
|
||||||
>
|
|
||||||
<template
|
|
||||||
v-if="
|
|
||||||
Number(scope.row.status) === 1 &&
|
|
||||||
String(scope.row.user_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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
<div class="pagination-container">
|
<div class="pagination-container">
|
||||||
<el-pagination
|
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :total="allTickets.length"
|
||||||
v-model:current-page="currentPage"
|
:page-sizes="[15, 30, 50]" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
|
||||||
v-model:page-size="pageSize"
|
@current-change="handleCurrentChange" background />
|
||||||
:total="filteredTickets.length"
|
|
||||||
:page-sizes="[15, 30, 50]"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
background
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -325,20 +220,23 @@ import {
|
|||||||
CircleCheck,
|
CircleCheck,
|
||||||
Checked,
|
Checked,
|
||||||
Loading,
|
Loading,
|
||||||
|
Close,
|
||||||
} from "@element-plus/icons-vue";
|
} from "@element-plus/icons-vue";
|
||||||
import { ticketApi, userApi } from "@/api/ticket";
|
import { ticketApi, userApi } from "@/api/ticket";
|
||||||
import { resetRouter } from "@/router";
|
import { resetRouter } from "@/router";
|
||||||
import { formatUrl, formatDate } from "@/utils/format";
|
import { formatUrl, formatDate } from "@/utils/format";
|
||||||
|
import SearchInput from "@/components/common/SearchInput.vue";
|
||||||
|
|
||||||
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 editValue = ref("");
|
const editValue = ref("");
|
||||||
|
const searchKeyword = ref("");
|
||||||
|
|
||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const pageSize = ref(15);
|
const pageSize = ref(15);
|
||||||
|
const realStaffInfo = ref(null);
|
||||||
const emptyText = computed(() => `暂无${currentStatus.value}的工单`);
|
const emptyText = computed(() => `暂无${currentStatus.value}的工单`);
|
||||||
|
|
||||||
// js计算时间
|
// js计算时间
|
||||||
@@ -354,13 +252,14 @@ const welcomeMessage = computed(() => {
|
|||||||
|
|
||||||
// 获取当前用户信息
|
// 获取当前用户信息
|
||||||
const currentUser = computed(() => {
|
const currentUser = computed(() => {
|
||||||
const info = localStorage.getItem("user_info") || sessionStorage.getItem("user_info");
|
const info = sessionStorage.getItem("user_info") || localStorage.getItem("user_info");
|
||||||
return info ? JSON.parse(info) : { id: null, username: "未知" };
|
return info ? JSON.parse(info) : { id: null, username: "未知", role: "未知" };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// 获取工单信息
|
// 获取工单信息
|
||||||
const fetchTickets = async () => {
|
const fetchTickets = async () => {
|
||||||
loading.value = true;
|
loading.value = true; // 显示加载中状态
|
||||||
// 优化:不再获取所有工单,而是根据当前视图的状态和用户ID进行筛选
|
// 优化:不再获取所有工单,而是根据当前视图的状态和用户ID进行筛选
|
||||||
try {
|
try {
|
||||||
const statusMap = {
|
const statusMap = {
|
||||||
@@ -370,28 +269,32 @@ const fetchTickets = async () => {
|
|||||||
已完结: 3,
|
已完结: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusValue =
|
const statusValue = statusMap[currentStatus.value]
|
||||||
statusMap[currentStatus.value] !== undefined
|
|
||||||
? statusMap[currentStatus.value]
|
|
||||||
: currentStatus.value;
|
|
||||||
|
|
||||||
const params = {
|
const params = { // 传递后端参数
|
||||||
status: statusValue,
|
status: statusValue,
|
||||||
limit: 1000, // 仍然获取足够的数据以支持前端分页
|
limit: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 对于非“未分配”的视图,只拉取当前登录员工的工单
|
// 如果不是未分配,则只拉取当前员工相关的工单
|
||||||
if (currentStatus.value !== "未分配") {
|
if (currentStatus.value !== "未分配") {
|
||||||
if (!realStaffInfo.value?.id) {
|
// 判断是否是员工
|
||||||
allTickets.value = []; // 如果没有员工信息,则清空列表
|
if (currentUser.value.role !== "user") {
|
||||||
|
allTickets.value = []; // 如果不是员工,则清空列表
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
params.user_id = realStaffInfo.value.id;
|
|
||||||
|
params.user_id = currentUser.value.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 针对员工搜索框
|
||||||
|
if (searchKeyword.value) {
|
||||||
|
params.search = searchKeyword.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await ticketApi.getTickets(params);
|
const res = await ticketApi.getTickets(params);
|
||||||
if (res.code === 200 || res.code === true) {
|
if (res.code === 200) {
|
||||||
allTickets.value = res.data.data || [];
|
allTickets.value = res.data.data || [];
|
||||||
} else {
|
} else {
|
||||||
allTickets.value = [];
|
allTickets.value = [];
|
||||||
@@ -405,31 +308,40 @@ const fetchTickets = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 过滤工单
|
|
||||||
const filteredTickets = computed(() => {
|
|
||||||
// 优化:由于 fetchTickets 已经从后端获取了经过筛选的数据,
|
|
||||||
// allTickets 本身就是过滤后的列表,无需再在客户端进行过滤。
|
|
||||||
return allTickets.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
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 allTickets.value.slice(start, end);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSizeChange = (val) => {
|
const handleSizeChange = (val) => {
|
||||||
pageSize.value = val;
|
pageSize.value = val;
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleCurrentChange = (val) => {
|
const handleCurrentChange = (val) => {
|
||||||
currentPage.value = val;
|
currentPage.value = val;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleStatusChange = async () => {
|
const handleStatusChange = async () => {
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
await fetchTickets();
|
await fetchTickets();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSearch = async () => {
|
||||||
|
currentPage.value = 1;
|
||||||
|
await fetchTickets();
|
||||||
|
};
|
||||||
|
const handleClearSearch = async () => {
|
||||||
|
searchKeyword.value = "";
|
||||||
|
currentPage.value = 1;
|
||||||
|
await fetchTickets();
|
||||||
|
};
|
||||||
|
|
||||||
const prepareEdit = (row, field) => {
|
const prepareEdit = (row, field) => {
|
||||||
editValue.value = row[field] || "";
|
editValue.value = row[field] || "";
|
||||||
};
|
};
|
||||||
@@ -458,7 +370,7 @@ const handleAccept = (row) => {
|
|||||||
ElMessageBox.confirm(`确认接手工单 #${row.id}?`).then(async () => {
|
ElMessageBox.confirm(`确认接手工单 #${row.id}?`).then(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await ticketApi.updateTicket(row.id, {
|
const res = await ticketApi.updateTicket(row.id, {
|
||||||
user_id: realStaffInfo.value.id,
|
user_id: currentUser.value.id,
|
||||||
status: 1,
|
status: 1,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
});
|
});
|
||||||
@@ -519,11 +431,12 @@ const handleCancel = (row) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 退出登录(已完成)
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
ElMessageBox.confirm("确定退出当前账号?", "提示", { type: "warning" }).then(async () => {
|
ElMessageBox.confirm("确定退出当前账号?", "提示", { type: "warning" }).then(async () => {
|
||||||
try {
|
try {
|
||||||
await userApi.logout();
|
await userApi.logout();
|
||||||
} catch (e) {}
|
} catch (e) { }
|
||||||
|
|
||||||
localStorage.removeItem("user_info");
|
localStorage.removeItem("user_info");
|
||||||
sessionStorage.removeItem("user_info");
|
sessionStorage.removeItem("user_info");
|
||||||
@@ -540,7 +453,7 @@ onMounted(async () => {
|
|||||||
const match = (res.data || []).find((s) => s.username === currentUser.value.username);
|
const match = (res.data || []).find((s) => s.username === currentUser.value.username);
|
||||||
if (match) realStaffInfo.value = match;
|
if (match) realStaffInfo.value = match;
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) { }
|
||||||
await fetchTickets();
|
await fetchTickets();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -565,7 +478,9 @@ onMounted(async () => {
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 28px 32px;
|
padding: 28px 32px;
|
||||||
display: flex; justify-content: space-between; align-items: center;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 4px 16px rgba(102, 126, 234, 0.2),
|
0 4px 16px rgba(102, 126, 234, 0.2),
|
||||||
@@ -579,13 +494,24 @@ onMounted(async () => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background:
|
background:
|
||||||
radial-gradient(circle at 20% 50%, rgba(255,255,255,0.08) 0%, transparent 50%),
|
radial-gradient(circle at 20% 50%, rgba(255, 255, 255, 0.08) 0%, transparent 50%),
|
||||||
radial-gradient(circle at 80% 50%, rgba(255,255,255,0.04) 0%, transparent 50%);
|
radial-gradient(circle at 80% 50%, rgba(255, 255, 255, 0.04) 0%, transparent 50%);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-main { display: flex; flex-direction: column; justify-content: center; position: relative; z-index: 1; }
|
.header-main {
|
||||||
.welcome-container { display: flex; flex-direction: column; gap: 8px; }
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.greeting {
|
.greeting {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -594,54 +520,87 @@ onMounted(async () => {
|
|||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
text-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-text {
|
.sub-text {
|
||||||
margin: 4px 0 0;
|
margin: 4px 0 0;
|
||||||
color: rgba(255,255,255,0.75);
|
color: rgba(255, 255, 255, 0.75);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 0.01em;
|
letter-spacing: 0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.identity-badges { margin-top: 8px; display: flex; align-items: center; position: relative; z-index: 1; }
|
.identity-badges {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.identity-badges .el-tag) {
|
:deep(.identity-badges .el-tag) {
|
||||||
display: inline-flex !important;
|
display: inline-flex !important;
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
gap: 5px !important;
|
gap: 5px !important;
|
||||||
background: rgba(255,255,255,0.18) !important;
|
background: rgba(255, 255, 255, 0.18) !important;
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
border: 1px solid rgba(255,255,255,0.25) !important;
|
border: 1px solid rgba(255, 255, 255, 0.25) !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
padding: 4px 14px !important;
|
padding: 4px 14px !important;
|
||||||
border-radius: 20px !important;
|
border-radius: 20px !important;
|
||||||
}
|
}
|
||||||
:deep(.identity-badges .el-tag .el-icon) { color: #fbbf24 !important; }
|
|
||||||
|
|
||||||
.header-action { position: relative; z-index: 1; }
|
:deep(.identity-badges .el-tag .el-icon) {
|
||||||
|
color: #fbbf24 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-action {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.header-action :deep(.el-button) {
|
.header-action :deep(.el-button) {
|
||||||
background: rgba(255,255,255,0.15) !important;
|
background: rgba(255, 255, 255, 0.15) !important;
|
||||||
border: 1px solid rgba(255,255,255,0.25) !important;
|
border: 1px solid rgba(255, 255, 255, 0.25) !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
border-radius: 24px !important;
|
border-radius: 24px !important;
|
||||||
padding: 8px 18px !important;
|
padding: 8px 18px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-action :deep(.el-button:hover) {
|
.header-action :deep(.el-button:hover) {
|
||||||
background: rgba(255,255,255,0.25) !important;
|
background: rgba(255, 255, 255, 0.25) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Tabs + Search Row ---- */
|
||||||
|
.tabs-toolbar-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-row :deep(.search-input) {
|
||||||
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Tab Switcher ---- */
|
/* ---- Tab Switcher ---- */
|
||||||
.status-tabs-wrapper {
|
.status-tabs-wrapper {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid rgba(0,0,0,0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: 18px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-radio-group :deep(.el-radio-button__inner) {
|
.custom-radio-group :deep(.el-radio-button__inner) {
|
||||||
@@ -649,7 +608,9 @@ onMounted(async () => {
|
|||||||
border-radius: 10px !important;
|
border-radius: 10px !important;
|
||||||
margin: 0 1px;
|
margin: 0 1px;
|
||||||
padding: 10px 18px;
|
padding: 10px 18px;
|
||||||
display: flex; align-items: center; gap: 6px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #6b6d7a;
|
color: #6b6d7a;
|
||||||
@@ -670,15 +631,18 @@ onMounted(async () => {
|
|||||||
/* ---- Table Card ---- */
|
/* ---- Table Card ---- */
|
||||||
.table-card {
|
.table-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid rgba(0,0,0,0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 20px 24px;
|
padding: 20px 24px;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 2px 8px rgba(0,0,0,0.03),
|
0 2px 8px rgba(0, 0, 0, 0.03),
|
||||||
0 4px 20px rgba(0,0,0,0.04);
|
0 4px 20px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-table { overflow: hidden; border-radius: var(--radius-md); }
|
.custom-table {
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-table__header th) {
|
:deep(.el-table__header th) {
|
||||||
background: #f8f9fc;
|
background: #f8f9fc;
|
||||||
@@ -690,66 +654,158 @@ onMounted(async () => {
|
|||||||
border-bottom: 2px solid #eef0f4 !important;
|
border-bottom: 2px solid #eef0f4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-table__body tr) { transition: all 0.15s; }
|
:deep(.el-table__body tr) {
|
||||||
:deep(.el-table__body tr:hover > td) {
|
transition: all 0.15s;
|
||||||
background: linear-gradient(90deg, rgba(102,126,234,0.03), rgba(118,75,162,0.02)) !important;
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__body tr:hover > td) {
|
||||||
|
background: linear-gradient(90deg, rgba(102, 126, 234, 0.03), rgba(118, 75, 162, 0.02)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__body tr) {
|
||||||
|
border-bottom: 1px solid #f5f5f8;
|
||||||
}
|
}
|
||||||
:deep(.el-table__body tr) { border-bottom: 1px solid #f5f5f8; }
|
|
||||||
|
|
||||||
/* Staggered row tints */
|
/* Staggered row tints */
|
||||||
:deep(.el-table__body tr:nth-child(odd) td) { background: #fdfdfe; }
|
:deep(.el-table__body tr:nth-child(odd) td) {
|
||||||
:deep(.el-table__body tr:nth-child(even) td) { background: #fafbfd; }
|
background: #fdfdfe;
|
||||||
|
}
|
||||||
.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%; }
|
:deep(.el-table__body tr:nth-child(even) td) {
|
||||||
.display-val { font-weight: 700; color: #667eea; font-size: 15px; font-family: "Courier New", monospace; }
|
background: #fafbfd;
|
||||||
.display-remark {
|
}
|
||||||
font-size: 12px; color: #5b5d68; max-width: 180px; line-height: 1.4;
|
|
||||||
background: #f8f9fc; padding: 4px 12px; border-radius: 6px; text-align: center;
|
.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: #667eea;
|
||||||
|
font-size: 15px;
|
||||||
|
font-family: "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-remark {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #5b5d68;
|
||||||
|
max-width: 180px;
|
||||||
|
line-height: 1.4;
|
||||||
|
background: #f8f9fc;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-data-placeholder {
|
||||||
|
color: #c0c4cc;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pure-date {
|
||||||
|
color: #6b6d7a;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: "Courier New", monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-link-btn {
|
||||||
|
font-size: 12px;
|
||||||
|
height: auto;
|
||||||
|
padding: 0;
|
||||||
|
opacity: 0.6;
|
||||||
|
color: #667eea;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-link-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
color: #4f46e5;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.no-data-placeholder { color: #c0c4cc; font-size: 12px; }
|
|
||||||
.pure-date { color: #6b6d7a; font-weight: 500; font-family: "Courier New", monospace; font-size: 12px; }
|
|
||||||
.edit-link-btn { font-size: 12px; height: auto; padding: 0; opacity: 0.6; color: #667eea; transition: all 0.15s; }
|
|
||||||
.edit-link-btn:hover { opacity: 1; color: #4f46e5; font-weight: 600; }
|
|
||||||
|
|
||||||
/* ---- Pagination ---- */
|
/* ---- Pagination ---- */
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
margin-top: 18px; display: flex; justify-content: flex-end;
|
margin-top: 18px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
border-top: 1px solid #f0f1f4;
|
border-top: 1px solid #f0f1f4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Progress ---- */
|
/* ---- Progress ---- */
|
||||||
:deep(.el-progress-bar__outer) { background: #eef0f4; border-radius: 10px; overflow: hidden; }
|
:deep(.el-progress-bar__outer) {
|
||||||
|
background: #eef0f4;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-progress-bar__inner) {
|
:deep(.el-progress-bar__inner) {
|
||||||
background: linear-gradient(90deg, #667eea, #764ba2) !important;
|
background: linear-gradient(90deg, #667eea, #764ba2) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-progress-bar__innerText) {
|
:deep(.el-progress-bar__innerText) {
|
||||||
display: flex !important; align-items: center !important;
|
display: flex !important;
|
||||||
justify-content: center !important; height: 100% !important;
|
align-items: center !important;
|
||||||
line-height: 1 !important; vertical-align: middle !important;
|
justify-content: center !important;
|
||||||
|
height: 100% !important;
|
||||||
|
line-height: 1 !important;
|
||||||
|
vertical-align: middle !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-progress-bar__innerText svg),
|
:deep(.el-progress-bar__innerText svg),
|
||||||
:deep(.el-progress-bar__innerText .el-icon) {
|
:deep(.el-progress-bar__innerText .el-icon) {
|
||||||
margin: 0 !important; position: relative !important;
|
margin: 0 !important;
|
||||||
top: -1px !important; width: 0.8em; height: 0.8em;
|
position: relative !important;
|
||||||
|
top: -1px !important;
|
||||||
|
width: 0.8em;
|
||||||
|
height: 0.8em;
|
||||||
}
|
}
|
||||||
:deep(.el-progress) { display: flex; align-items: center; }
|
|
||||||
|
|
||||||
.progress-radio-group { display: flex; justify-content: center; margin: 10px 0; }
|
:deep(.el-progress) {
|
||||||
.progress-radio-group :deep(.el-radio-button__inner) { padding: 8px 10px !important; }
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-radio-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-radio-group :deep(.el-radio-button__inner) {
|
||||||
|
padding: 8px 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Table Overrides ---- */
|
/* ---- Table Overrides ---- */
|
||||||
:deep(.el-table__row) { height: 100px; }
|
:deep(.el-table__row) {
|
||||||
:deep(.el-table .cell) { overflow: visible !important; }
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table .cell) {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Popover (Inline Edit Flyout) ---- */
|
/* ---- Popover (Inline Edit Flyout) ---- */
|
||||||
:deep(.el-popover) {
|
:deep(.el-popover) {
|
||||||
border-radius: 16px !important;
|
border-radius: 16px !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 8px 32px rgba(0,0,0,0.1),
|
0 8px 32px rgba(0, 0, 0, 0.1),
|
||||||
0 2px 8px rgba(0,0,0,0.06) !important;
|
0 2px 8px rgba(0, 0, 0, 0.06) !important;
|
||||||
padding: 20px !important;
|
padding: 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -770,50 +826,57 @@ onMounted(async () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pop-header::before {
|
.pop-header::before {
|
||||||
content: "";
|
content: "";
|
||||||
width: 3px; height: 14px;
|
width: 3px;
|
||||||
|
height: 14px;
|
||||||
background: linear-gradient(180deg, #667eea, #764ba2);
|
background: linear-gradient(180deg, #667eea, #764ba2);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.popover-content .el-input__wrapper) {
|
:deep(.popover-content .el-input__wrapper) {
|
||||||
border-radius: 10px !important;
|
border-radius: 10px !important;
|
||||||
box-shadow: 0 0 0 1px rgba(0,0,0,0.08) inset !important;
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08) inset !important;
|
||||||
transition: box-shadow 0.2s;
|
transition: box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.popover-content .el-input__wrapper:hover) {
|
:deep(.popover-content .el-input__wrapper:hover) {
|
||||||
box-shadow: 0 0 0 1px rgba(102,126,234,0.3) inset !important;
|
box-shadow: 0 0 0 1px rgba(102, 126, 234, 0.3) inset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.popover-content .el-input__wrapper.is-focus) {
|
:deep(.popover-content .el-input__wrapper.is-focus) {
|
||||||
box-shadow: 0 0 0 2px rgba(102,126,234,0.15), 0 0 0 1px #667eea inset !important;
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.15), 0 0 0 1px #667eea inset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.popover-content .el-textarea__inner) {
|
:deep(.popover-content .el-textarea__inner) {
|
||||||
border-radius: 10px !important;
|
border-radius: 10px !important;
|
||||||
box-shadow: 0 0 0 1px rgba(0,0,0,0.08) inset !important;
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08) inset !important;
|
||||||
transition: box-shadow 0.2s;
|
transition: box-shadow 0.2s;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.popover-content .el-textarea__inner:focus) {
|
:deep(.popover-content .el-textarea__inner:focus) {
|
||||||
box-shadow: 0 0 0 2px rgba(102,126,234,0.15), 0 0 0 1px #667eea inset !important;
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.15), 0 0 0 1px #667eea inset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pop-footer {
|
.pop-footer {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pop-footer :deep(.el-button--primary) {
|
.pop-footer :deep(.el-button--primary) {
|
||||||
background: linear-gradient(135deg, #667eea, #764ba2) !important;
|
background: linear-gradient(135deg, #667eea, #764ba2) !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
border-radius: 10px !important;
|
border-radius: 10px !important;
|
||||||
padding: 7px 20px !important;
|
padding: 7px 20px !important;
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
box-shadow: 0 2px 8px rgba(102,126,234,0.3);
|
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pop-footer :deep(.el-button--primary:hover) {
|
.pop-footer :deep(.el-button--primary:hover) {
|
||||||
box-shadow: 0 4px 16px rgba(102,126,234,0.4);
|
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,27 +885,40 @@ onMounted(async () => {
|
|||||||
border-radius: 8px !important;
|
border-radius: 8px !important;
|
||||||
padding: 6px 12px !important;
|
padding: 6px 12px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.popover-content .el-radio-button__inner:hover) {
|
:deep(.popover-content .el-radio-button__inner:hover) {
|
||||||
color: #667eea !important;
|
color: #667eea !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.popover-content .is-active .el-radio-button__inner) {
|
:deep(.popover-content .is-active .el-radio-button__inner) {
|
||||||
background: linear-gradient(135deg, #667eea, #764ba2) !important;
|
background: linear-gradient(135deg, #667eea, #764ba2) !important;
|
||||||
border-color: transparent !important;
|
border-color: transparent !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
box-shadow: 0 2px 6px rgba(102,126,234,0.3);
|
box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Action Buttons Glow ---- */
|
/* ---- Action Buttons Glow ---- */
|
||||||
:deep(.el-table .el-button--primary) { transition: all 0.2s; }
|
:deep(.el-table .el-button--primary) {
|
||||||
:deep(.el-table .el-button--primary:hover) { box-shadow: 0 0 12px var(--glow-indigo); }
|
transition: all 0.2s;
|
||||||
:deep(.el-table .el-button--success:hover) { box-shadow: 0 0 12px var(--glow-green); }
|
}
|
||||||
:deep(.el-table .el-button--warning:hover) { box-shadow: 0 0 12px var(--glow-amber); }
|
|
||||||
|
:deep(.el-table .el-button--primary:hover) {
|
||||||
|
box-shadow: 0 0 12px var(--glow-indigo);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table .el-button--success:hover) {
|
||||||
|
box-shadow: 0 0 12px var(--glow-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table .el-button--warning:hover) {
|
||||||
|
box-shadow: 0 0 12px var(--glow-amber);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Logout Dropdown ---- */
|
/* ---- Logout Dropdown ---- */
|
||||||
:deep(.el-dropdown-menu) {
|
:deep(.el-dropdown-menu) {
|
||||||
border-radius: 14px !important;
|
border-radius: 14px !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
box-shadow: 0 8px 32px rgba(0,0,0,0.1), 0 2px 8px rgba(0,0,0,0.06) !important;
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1), 0 2px 8px rgba(0, 0, 0, 0.06) !important;
|
||||||
padding: 6px !important;
|
padding: 6px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -855,7 +931,7 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-dropdown-menu__item:hover) {
|
:deep(.el-dropdown-menu__item:hover) {
|
||||||
background: linear-gradient(135deg, rgba(244,63,94,0.06), rgba(244,63,94,0.02)) !important;
|
background: linear-gradient(135deg, rgba(244, 63, 94, 0.06), rgba(244, 63, 94, 0.02)) !important;
|
||||||
color: #e11d48 !important;
|
color: #e11d48 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,7 +939,7 @@ onMounted(async () => {
|
|||||||
:deep(.el-message-box) {
|
:deep(.el-message-box) {
|
||||||
border-radius: 18px !important;
|
border-radius: 18px !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
box-shadow: 0 12px 48px rgba(0,0,0,0.12), 0 4px 16px rgba(0,0,0,0.06) !important;
|
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.12), 0 4px 16px rgba(0, 0, 0, 0.06) !important;
|
||||||
padding: 28px !important;
|
padding: 28px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,10 +969,10 @@ onMounted(async () => {
|
|||||||
:deep(.el-message-box__btns .el-button--primary) {
|
:deep(.el-message-box__btns .el-button--primary) {
|
||||||
background: linear-gradient(135deg, #667eea, #764ba2) !important;
|
background: linear-gradient(135deg, #667eea, #764ba2) !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
box-shadow: 0 2px 8px rgba(102,126,234,0.3) !important;
|
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-message-box__btns .el-button--primary:hover) {
|
:deep(.el-message-box__btns .el-button--primary:hover) {
|
||||||
box-shadow: 0 4px 16px rgba(102,126,234,0.4) !important;
|
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4) !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user