Files
admin-ui/src/views/customerService/script/index.vue

289 lines
7.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="system-role-container">
<el-card shadow="hover">
<!-- 搜索和操作区域 -->
<div class="system-user-search mb15">
<el-form :inline="true">
<el-form-item label="标签">
<el-input size="default" v-model="tableData.param.tag" placeholder="请输入标签" class="w-50 m-2" clearable @keyup.enter="handleSearch" />
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="handleSearch" :loading="tableData.loading">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" class="ml10" @click="handleReset" :disabled="tableData.loading">
<el-icon>
<ele-Refresh />
</el-icon>
重置
</el-button>
<el-button size="default" type="success" @click="handleAdd">
<el-icon><FolderAdd /></el-icon>
新增话术
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 数据表格 -->
<el-table :data="tableData.data" v-loading="tableData.loading" style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="tag" label="标签" show-overflow-tooltip min-width="120" />
<el-table-column prop="creator" label="创建人" show-overflow-tooltip min-width="100" />
<el-table-column prop="updater" label="修改人" show-overflow-tooltip min-width="100" />
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip min-width="140">
<template #default="{ row }">
{{ formatTime(row.createdAt) }}
</template>
</el-table-column>
<el-table-column prop="updatedAt" label="修改时间" show-overflow-tooltip min-width="140">
<template #default="{ row }">
{{ formatTime(row.updatedAt) }}
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<el-button size="small" text type="primary" class="op-btn-edit" @click="handleEdit(row)">
<el-icon><EditPen /></el-icon>修改
</el-button>
<el-button size="small" text type="danger" class="op-btn-del" @click="handleDelete(row)">
<el-icon><DeleteFilled /></el-icon>删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-if="tableData.total > 0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="loadTableData"
/>
</el-card>
<!-- 编辑组件 -->
<EditRole ref="editRoleRef" @refresh="loadTableData" />
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import { FolderAdd, EditPen, DeleteFilled } from '@element-plus/icons-vue';
import EditRole from './component/editRole.vue';
import Pagination from '/@/components/pagination/index.vue';
import { getscriptList, deleteScript } from '/@/api/customerService/script';
// ==================== 类型定义 ====================
interface ScriptItem {
id: string;
tag: string;
creator: string;
modifier: string;
createdAt: string; // 保持原字段不变
updatedAt: string; // 保持原字段不变
content?: string;
}
interface TableParams {
pageNum: number;
pageSize: number;
tag: '';
}
interface TableState {
data: ScriptItem[];
total: number;
loading: boolean;
param: TableParams;
}
// ==================== 响应式数据 ====================
const editRoleRef = ref<InstanceType<typeof EditRole>>();
const tableData = reactive<TableState>({
data: [],
total: 0,
loading: false,
param: {
pageNum: 1,
pageSize: 10,
tag: '',
},
});
/**
* 处理搜索
*/
const handleSearch = () => {
tableData.param.pageNum = 1; // 搜索时重置到第一页
loadTableData();
};
/**
* 重置查询条件
*/
const handleReset = () => {
// 重新获取数据
tableData.param = { pageNum: 1, pageSize: 10, tag: '' };
loadTableData();
};
// ==================== 时间处理函数 ====================
/**
* 格式化时间显示
* @param time 时间字符串或时间戳
* @returns 格式化后的时间字符串
*/
const formatTime = (time: string | number | Date): string => {
if (!time) return '-';
try {
let date: Date;
if (time instanceof Date) {
date = time;
} else if (typeof time === 'string') {
// 直接使用字符串创建Date对象ISO格式会自动识别
date = new Date(time);
} else {
// 处理时间戳
let timestamp = time;
if (timestamp > 1000000000000) {
// 已经是毫秒时间戳
} else if (timestamp > 1000000000) {
// 秒时间戳,转换为毫秒
timestamp = timestamp * 1000;
}
date = new Date(timestamp);
}
if (isNaN(date.getTime())) {
return String(time);
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} catch (error) {
console.error('时间格式化错误:', error);
return String(time);
}
};
// ==================== 方法定义 ====================
/**
* 加载表格数据
*/
const loadTableData = async () => {
try {
tableData.loading = true;
const response = await getscriptList(tableData.param);
const { list = [], total = 0 } = response.data || {};
tableData.data = list;
tableData.total = total;
} catch (error) {
console.error('加载数据失败:', error);
// 错误已由请求拦截器统一处理
tableData.data = [];
tableData.total = 0;
} finally {
tableData.loading = false;
}
};
/**
* 新增话术
*/
const handleAdd = () => {
editRoleRef.value?.openDialog();
};
/**
* 编辑话术
*/
const handleEdit = (row: ScriptItem) => {
editRoleRef.value?.openDialog(row as any);
};
/**
* 删除话术
*/
const handleDelete = async (row: ScriptItem) => {
try {
await ElMessageBox.confirm(`确定要删除话术「${row.tag}」吗?此操作不可恢复。`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
await deleteScript({ id: row.id });
ElMessage.success('删除成功');
// 重新加载数据
await loadTableData();
} catch (error) {
if (error !== 'cancel') {
console.error('删除失败:', error);
// 错误已由请求拦截器统一处理
}
}
};
/**
* 操作成功回调
*/
const handleSuccess = () => {
loadTableData();
};
// ==================== 生命周期 ====================
onMounted(() => {
loadTableData();
});
// ==================== 暴露方法(可选) ====================
defineExpose({
reload: loadTableData,
});
</script>
<style scoped lang="scss">
.system-role-container {
.system-user-search {
display: flex;
justify-content: space-between;
align-items: center;
}
.mb15 {
margin-bottom: 15px;
}
}
// 响应式适配
@media (max-width: 768px) {
.system-user-search {
flex-direction: column;
align-items: flex-start;
.el-form-item {
margin-bottom: 10px;
}
}
}
</style>