fix:修改
This commit is contained in:
2
src/auto-imports.d.ts
vendored
2
src/auto-imports.d.ts
vendored
@@ -7,8 +7,10 @@
|
|||||||
export {}
|
export {}
|
||||||
declare global {
|
declare global {
|
||||||
const EffectScope: typeof import('vue').EffectScope
|
const EffectScope: typeof import('vue').EffectScope
|
||||||
|
const ElButton: typeof import('element-plus/es').ElButton
|
||||||
const ElMessage: typeof import('element-plus/es').ElMessage
|
const ElMessage: typeof import('element-plus/es').ElMessage
|
||||||
const ElMessageBox: typeof import('element-plus/es').ElMessageBox
|
const ElMessageBox: typeof import('element-plus/es').ElMessageBox
|
||||||
|
const ElTag: typeof import('element-plus/es').ElTag
|
||||||
const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate
|
const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate
|
||||||
const computed: typeof import('vue').computed
|
const computed: typeof import('vue').computed
|
||||||
const createApp: typeof import('vue').createApp
|
const createApp: typeof import('vue').createApp
|
||||||
|
|||||||
@@ -4,123 +4,368 @@
|
|||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:columns="adaptedColumns"
|
:columns="adaptedColumns"
|
||||||
:data="data"
|
:data="innerData"
|
||||||
:width="tableSize.width"
|
:width="tableSize.width"
|
||||||
:height="tableSize.height"
|
:height="tableSize.height"
|
||||||
:fixed="true"
|
:fixed="true"
|
||||||
@rows-rendered="handleRowsRendered"
|
@rows-rendered="handleRowsRendered"
|
||||||
>
|
>
|
||||||
<template #overlay v-if="loading">
|
<template #overlay v-if="loading && innerData.length === 0">
|
||||||
<div class="v2-loading-overlay">
|
<div class="v2-initial-loading">
|
||||||
<el-icon class="is-loading" :size="26"><Loading /></el-icon>
|
<div class="loading-content">
|
||||||
|
<el-icon class="is-loading" :size="30"><Loading /></el-icon>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-v2>
|
|
||||||
|
|
||||||
<div class="pro-table-v2-footer">
|
<template #footer>
|
||||||
<div class="footer-left">
|
<div class="v2-infinite-footer">
|
||||||
<span>已加载 {{ data.length }} / 共 {{ total }} 条</span>
|
<div v-if="loading && innerData.length > 0" class="loading-more">
|
||||||
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-right">
|
<div v-else-if="noMore" class="no-more">
|
||||||
<span v-if="loading">加载中...</span>
|
<span class="line"></span>
|
||||||
<span v-else-if="noMore">已加载全部</span>
|
<span class="text">已经到底了~</span>
|
||||||
|
<span class="line"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无相关数据" :image-size="100" />
|
||||||
|
</template>
|
||||||
|
</el-table-v2>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { ref, computed, onMounted, onUnmounted } from "vue";
|
import { ref, computed, onMounted, onUnmounted, nextTick } from "vue";
|
||||||
import { debounce } from 'lodash-es';
|
import { ElTag, ElButton, ElText } from "element-plus";
|
||||||
import { ElTag, ElText } from 'element-plus';
|
import { debounce } from "lodash-es";
|
||||||
import NameAvatar from "@/components/NameAvatar/index.vue";
|
import dayjs from "dayjs";
|
||||||
import dayjs from 'dayjs';
|
import { DictManage } from "@/dict";
|
||||||
|
|
||||||
|
// 组件属性
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
columns: { type: Array, required: true },
|
columns: { type: Array, required: true },
|
||||||
data: { type: Array, required: true },
|
requestApi: { type: Function, required: true },
|
||||||
total: { type: Number, default: 0 },
|
initParam: { type: Object, default: () => ({}) },
|
||||||
loading: { type: Boolean, default: false },
|
pageSize: { type: Number, default: 20 },
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["load-more"]);
|
// 内部数据状态
|
||||||
|
const tableRef = ref(null);
|
||||||
|
const tableContainerRef = ref(null);
|
||||||
|
const loading = ref(false);
|
||||||
|
const innerData = ref<any[]>([]);
|
||||||
|
const total = ref(0);
|
||||||
|
const pageNo = ref(1);
|
||||||
|
const tableSize = ref({ width: 0, height: 400 });
|
||||||
|
|
||||||
// --- 内置渲染逻辑工厂 ---
|
const noMore = computed(
|
||||||
const builtInRenderers = {
|
() => innerData.value.length >= total.value && total.value > 0
|
||||||
// 1. 人员头像渲染器 (默认读取 row.name 和 row.avatar)
|
);
|
||||||
member: (scope: any, col: any) => (
|
// --- 1. 核心渲染工厂 (内置常用业务组件) ---
|
||||||
<div style="display:flex; align-items:center; gap:8px;">
|
const RenderFactory = {
|
||||||
<NameAvatar
|
// 状态点/标签
|
||||||
name={scope.rowData[col.prop]}
|
|
||||||
src={scope.rowData[col.avatarKey || 'avatar']}
|
|
||||||
size={28}
|
|
||||||
/>
|
|
||||||
<span>{scope.rowData[col.prop]}</span>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
|
|
||||||
// 2. 状态标签渲染器 (支持自定义映射)
|
|
||||||
status: (scope: any, col: any) => {
|
status: (scope: any, col: any) => {
|
||||||
const val = scope.rowData[col.prop];
|
const val = scope.rowData[col.prop];
|
||||||
const option = col.options?.find((opt: any) => opt.value === val) || { label: val, type: 'info' };
|
let rawOptions = col.options;
|
||||||
return <ElTag type={option.type} size="small">{option.label}</ElTag>;
|
// 如果是 ref 或 computed,先取 .value
|
||||||
},
|
if (rawOptions && typeof rawOptions === "object" && "value" in rawOptions) {
|
||||||
|
rawOptions = rawOptions.value;
|
||||||
|
}
|
||||||
|
// 如果是函数则执行
|
||||||
|
if (typeof rawOptions === "function") {
|
||||||
|
rawOptions = rawOptions();
|
||||||
|
}
|
||||||
|
|
||||||
// 3. 日期格式化
|
// 强制转为数组,防止 find 报错
|
||||||
|
const currentOptions = Array.isArray(rawOptions) ? rawOptions : [];
|
||||||
|
const target = currentOptions.find((opt: any) => opt.value == val);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="mj-status-dot"
|
||||||
|
style={{
|
||||||
|
"--data-status-color": DictManage.statusDictColor[target?.value],
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (col.onClick) {
|
||||||
|
col.onClick({ cellValue: val, rowData: scope.rowData });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{target?.label || "-"}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// 日期格式化
|
||||||
date: (scope: any, col: any) => {
|
date: (scope: any, col: any) => {
|
||||||
const val = scope.rowData[col.prop];
|
const val = scope.rowData[col.prop];
|
||||||
return <span>{val ? dayjs(val).format(col.format || 'YYYY-MM-DD HH:mm') : '-'}</span>;
|
return (
|
||||||
|
<span>
|
||||||
|
{val ? dayjs(val).format(col.format || "YYYY-MM-DD HH:mm") : "-"}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
// 文本自动省略
|
||||||
|
ellipsis: (scope: any, col: any) => {
|
||||||
|
const val = scope.rowData[col.prop] || "-";
|
||||||
|
return (
|
||||||
|
<div class="mj-ellipsis" title={val}>
|
||||||
|
{val}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// 4. 金额格式化
|
/**
|
||||||
money: (scope: any, col: any) => {
|
* 局部更新某一行数据
|
||||||
const val = scope.rowData[col.prop];
|
* @param id 唯一标识
|
||||||
return <ElText type="warning">¥ {Number(val || 0).toLocaleString()}</ElText>;
|
* @param rowData 新的数据对象(可以是部分属性)
|
||||||
|
*/
|
||||||
|
const updateRow = (id: string | number, rowData: object) => {
|
||||||
|
const index = innerData.value.findIndex((item) => item.id === id);
|
||||||
|
if (index !== -1) {
|
||||||
|
// 使用合并方式,保留原有的其他字段
|
||||||
|
innerData.value[index] = { ...innerData.value[index], ...rowData };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除某一行数据
|
||||||
|
* @param id 唯一标识
|
||||||
|
*/
|
||||||
|
const removeRow = (id: string | number) => {
|
||||||
|
const index = innerData.value.findIndex(item => item.id == id);
|
||||||
|
if (index !== -1) {
|
||||||
|
innerData.value.splice(index, 1);
|
||||||
|
total.value = Math.max(0, total.value - 1);
|
||||||
|
|
||||||
|
// 💡 关键:删除后如果当前显示的数据太少(比如不足一屏),自动去接下一页
|
||||||
|
if (innerData.value.length < 15 && !noMore.value) {
|
||||||
|
fetchTableData(false); // 这里的 false 表示“追加”数据来补位
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果删除后这一页空了,且还有数据,也去拉取
|
||||||
|
if (innerData.value.length === 0 && total.value > 0) {
|
||||||
|
fetchTableData(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入一条新数据(通常用于新增成功后插到最前面)
|
||||||
|
* @param rowData 完整的数据对象
|
||||||
|
*/
|
||||||
|
const addRow = (rowData: object) => {
|
||||||
|
innerData.value.unshift(rowData);
|
||||||
|
total.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 适配 Columns 配置 ---
|
||||||
const adaptedColumns = computed(() => {
|
const adaptedColumns = computed(() => {
|
||||||
return props.columns.map((col: any) => ({
|
// 1. 获取容器当前实际宽度
|
||||||
|
const containerWidth = tableSize.value.width;
|
||||||
|
if (containerWidth <= 0) return [];
|
||||||
|
|
||||||
|
// 2. 找出没有设置宽度的列
|
||||||
|
const autoColumns = props.columns.filter((col: any) => !col.width);
|
||||||
|
// 3. 计算已经固定了宽度的列总和
|
||||||
|
const fixedWidthTotal = props.columns
|
||||||
|
.filter((col: any) => col.width)
|
||||||
|
.reduce((prev, curr: any) => prev + Number(curr.width), 0);
|
||||||
|
// 4. 计算剩余可用宽度
|
||||||
|
const remainingWidth = Math.max(containerWidth - fixedWidthTotal, 0);
|
||||||
|
// 5. 计算每个自适应列应该分配到的平均宽度
|
||||||
|
const perAutoWidth = autoColumns.length > 0
|
||||||
|
? Math.floor(remainingWidth / autoColumns.length)
|
||||||
|
: 0;
|
||||||
|
return props.columns.map((col: any) => {
|
||||||
|
const isAuto = !col.width;
|
||||||
|
const finalWidth = isAuto ? Math.max(perAutoWidth, 100) : Number(col.width);
|
||||||
|
return {
|
||||||
key: col.prop,
|
key: col.prop,
|
||||||
dataKey: col.prop,
|
dataKey: col.prop,
|
||||||
title: col.label,
|
title: col.label,
|
||||||
width: col.width || 150,
|
width: finalWidth,
|
||||||
|
flexGrow: isAuto ? 1 : 0,
|
||||||
|
flexShrink: 1,
|
||||||
fixed: col.fixed,
|
fixed: col.fixed,
|
||||||
align: col.align || 'left',
|
align: col.align || "left",
|
||||||
cellRenderer: (scope: any) => {
|
cellRenderer: (scope: any) => {
|
||||||
// 优先级 1: 用户自定义了 render 函数
|
if (typeof col.render === "function") return col.render(scope);
|
||||||
if (typeof col.render === 'function') return col.render(scope);
|
if (col.valueType && RenderFactory[col.valueType]) {
|
||||||
|
return RenderFactory[col.valueType](scope, col);
|
||||||
// 优先级 2: 使用内置的 valueType 渲染器
|
|
||||||
if (col.valueType && builtInRenderers[col.valueType]) {
|
|
||||||
return builtInRenderers[col.valueType](scope, col);
|
|
||||||
}
|
}
|
||||||
|
return <span class="v2-cell-text">{scope.rowData[col.prop] ?? "-"}</span>;
|
||||||
// 优先级 3: 默认展示
|
|
||||||
return scope.rowData[col.prop] ?? '-';
|
|
||||||
},
|
},
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- 容器与滚动逻辑 (保持之前的一致) ---
|
// --- 请求逻辑 ---
|
||||||
const tableContainerRef = ref(null);
|
const fetchTableData = async (isReset = false) => {
|
||||||
const tableSize = ref({ width: 0, height: 400 });
|
if (loading.value) return;
|
||||||
const noMore = computed(() => props.data.length >= props.total && props.total > 0);
|
if (!isReset && noMore.value) return;
|
||||||
|
|
||||||
|
if (isReset) {
|
||||||
|
pageNo.value = 1;
|
||||||
|
noMore.value = false;
|
||||||
|
// 重置时不立即清空数据,防止闪烁
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
pageNo: pageNo.value,
|
||||||
|
pageSize: props.pageSize,
|
||||||
|
...props.initParam,
|
||||||
|
};
|
||||||
|
const res = await props.requestApi(params);
|
||||||
|
const records = res?.records || [];
|
||||||
|
|
||||||
|
innerData.value = isReset ? records : [...innerData.value, ...records];
|
||||||
|
total.value = res?.total || 0;
|
||||||
|
if (innerData.value.length >= total.value || newList.length < props.pageSize) {
|
||||||
|
noMore.value = true;
|
||||||
|
}
|
||||||
|
pageNo.value++;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 交互逻辑 ---
|
||||||
const updateSize = () => {
|
const updateSize = () => {
|
||||||
if (tableContainerRef.value) {
|
if (tableContainerRef.value) {
|
||||||
|
// 1. offsetWidth 包含 border 和 padding,是最准确的外盒宽度
|
||||||
|
const containerWidth = tableContainerRef.value.offsetWidth;
|
||||||
|
|
||||||
|
// 不要减 2,让表格填满
|
||||||
|
tableSize.value.width = containerWidth;
|
||||||
|
|
||||||
const rect = tableContainerRef.value.getBoundingClientRect();
|
const rect = tableContainerRef.value.getBoundingClientRect();
|
||||||
tableSize.value.width = rect.width;
|
const remainingHeight = window.innerHeight - rect.top - 24;
|
||||||
tableSize.value.height = window.innerHeight - rect.top - 45;
|
tableSize.value.height = Math.max(remainingHeight, 200);
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleResize = debounce(updateSize, 200);
|
|
||||||
const handleRowsRendered = ({ endRowIndex }: any) => {
|
|
||||||
if (endRowIndex >= props.data.length - 5 && !props.loading && !noMore.value) {
|
|
||||||
emit("load-more");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => { updateSize(); window.addEventListener('resize', handleResize); });
|
const handleRowsRendered = ({ endRowIndex }: any) => {
|
||||||
onUnmounted(() => { window.removeEventListener('resize', handleResize); });
|
// 阈值调大一点(10),保证滚动流畅,用户无感知加载
|
||||||
|
if (endRowIndex >= innerData.value.length - 10) {
|
||||||
|
fetchTableData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResize = debounce(updateSize, 200);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
updateSize();
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
fetchTableData(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => window.removeEventListener("resize", handleResize));
|
||||||
|
|
||||||
|
// 暴露 API
|
||||||
|
defineExpose({
|
||||||
|
refresh: () => fetchTableData(true),
|
||||||
|
updateRow,
|
||||||
|
removeRow,
|
||||||
|
addRow,
|
||||||
|
getCurrentParams: () => ({
|
||||||
|
pageNo: pageNo.value - 1, // 因为 fetch 完后 pageNo 会自增,所以要减 1 才是当前页
|
||||||
|
pageSize: props.pageSize,
|
||||||
|
...props.initParam
|
||||||
|
}),
|
||||||
|
innerData
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.pro-table-v2-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
/* 移除外边框,让样式更融入页面 */
|
||||||
|
|
||||||
|
// 1. 初始全屏 Loading
|
||||||
|
.v2-initial-loading {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
.loading-content {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
p {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 底部流式加载样式
|
||||||
|
.v2-infinite-footer {
|
||||||
|
padding: 20px 0;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
.loading-more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
.is-loading {
|
||||||
|
animation: rotating 2s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 15px;
|
||||||
|
opacity: 0.6;
|
||||||
|
|
||||||
|
.line {
|
||||||
|
width: 30px;
|
||||||
|
height: 1px;
|
||||||
|
background-color: #dcdfe6;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 适配 el-table-v2 核心样式
|
||||||
|
:deep(.el-table-v2__main) {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table-v2__header-wrapper) {
|
||||||
|
border-bottom: 1px solid #f2f3f5;
|
||||||
|
.el-table-v2__header-cell {
|
||||||
|
background-color: #f8fafc;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table-v2__row) {
|
||||||
|
border-bottom: 1px solid #f9fafb;
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
65
src/hooks/useTableAction.ts
Normal file
65
src/hooks/useTableAction.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tableRef ProTableV2 的组件实例引用
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface ActionOptions {
|
||||||
|
showMsg?: boolean; // 是否显示操作成功提示
|
||||||
|
}
|
||||||
|
export const useTableAction = (tableRef: any) => {
|
||||||
|
/**
|
||||||
|
* 执行操作并静默更新单行数据
|
||||||
|
* @param apiPromise 调用的操作接口(如状态切换、编辑提交)
|
||||||
|
* @param id 当前行 ID
|
||||||
|
* @param fetchDataApi 可选:列表请求函数,若传入则在成功后自动拉取最新行数据
|
||||||
|
*/
|
||||||
|
const handleAction = async (
|
||||||
|
api: Promise<any> | (() => Promise<any>),
|
||||||
|
id?: string | number,
|
||||||
|
fetchDataApi?: Function,
|
||||||
|
options = { showMsg: true }
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const res = typeof api === 'function' ? await api() : await api;
|
||||||
|
if (options.showMsg) ElMessage.success("操作成功");
|
||||||
|
|
||||||
|
// 局部更新逻辑... (同上)
|
||||||
|
if (id && fetchDataApi) {
|
||||||
|
const currentParams = tableRef.value?.getCurrentParams();
|
||||||
|
const listRes = await fetchDataApi(currentParams);
|
||||||
|
const records = listRes?.records || listRes?.data?.records || [];
|
||||||
|
const latestRowData = records.find((item: any) => item.id == id);
|
||||||
|
if (latestRowData) tableRef.value.updateRow(id, latestRowData);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
throw error; // 抛出错误让外部 catch
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装删除逻辑
|
||||||
|
* @param id 行 ID
|
||||||
|
* @param deleteApi 删除接口函数
|
||||||
|
*/
|
||||||
|
const handleDelete = async (
|
||||||
|
id: string | number,
|
||||||
|
deleteApi: (id: any) => Promise<any>
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const res = await deleteApi(id);
|
||||||
|
tableRef.value?.removeRow(id);
|
||||||
|
return res;
|
||||||
|
} catch (error) {
|
||||||
|
// 捕获取消行为或接口错误
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleAction,
|
||||||
|
handleDelete,
|
||||||
|
};
|
||||||
|
};
|
||||||
13
src/pages/businessManage/businessOpport/index.vue
Normal file
13
src/pages/businessManage/businessOpport/index.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="">
|
||||||
|
商机管理
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {reactive,ref,onMounted} from "vue"
|
||||||
|
|
||||||
|
defineOptions({})
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
13
src/pages/businessManage/customer/index.vue
Normal file
13
src/pages/businessManage/customer/index.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="">
|
||||||
|
客户管理
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {reactive,ref,onMounted} from "vue"
|
||||||
|
|
||||||
|
defineOptions({})
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
13
src/pages/businessManage/gameStudios/index.vue
Normal file
13
src/pages/businessManage/gameStudios/index.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="">
|
||||||
|
游戏和工作室
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {reactive,ref,onMounted} from "vue"
|
||||||
|
|
||||||
|
defineOptions({})
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -115,7 +115,7 @@ const onConfirm = async (formEl: FormInstance | undefined) => {
|
|||||||
onCancel();
|
onCancel();
|
||||||
emit('confirm-success');
|
emit('confirm-success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('error',error);
|
console.log('error',row.id);
|
||||||
} finally{
|
} finally{
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,9 @@
|
|||||||
<el-option
|
<el-option
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
v-for="(item, index) in dicts.permission_list_enable_disable"
|
v-for="(
|
||||||
|
item, index
|
||||||
|
) in dicts.permission_list_enable_disable"
|
||||||
:key="index"
|
:key="index"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -64,34 +66,10 @@
|
|||||||
<CommonTable
|
<CommonTable
|
||||||
ref="dictTableRef"
|
ref="dictTableRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
v-model:data="dataValue"
|
:data="dataValue"
|
||||||
v-model:total="total"
|
:total="total"
|
||||||
pagination
|
|
||||||
:request-api="getTableData"
|
:request-api="getTableData"
|
||||||
>
|
>
|
||||||
<!-- 编号内容显示 -->
|
|
||||||
<template #number="{ row, index }">
|
|
||||||
<span>#{{ formatIndex(index) }}</span>
|
|
||||||
</template>
|
|
||||||
<template #name="{ row }">
|
|
||||||
<span style="font-weight: 600">{{ row.name }}</span>
|
|
||||||
</template>
|
|
||||||
<!-- 编码标识 -->
|
|
||||||
<template #code="{ row }">
|
|
||||||
<el-tag size="small" type="info">{{ row.key }}</el-tag>
|
|
||||||
</template>
|
|
||||||
<!-- 状态插槽 -->
|
|
||||||
<template #status="{ row }">
|
|
||||||
<div
|
|
||||||
class="mj-status-dot"
|
|
||||||
:style="{
|
|
||||||
'--data-status-color': DictManage.statusDictColor[row.status],
|
|
||||||
}"
|
|
||||||
@click="handleDictStatus(row)"
|
|
||||||
>
|
|
||||||
{{ dicts.permission_list_enable_disable.find(item=>item.value == row.status)?.label }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</CommonTable>
|
</CommonTable>
|
||||||
|
|
||||||
<!-- 新增-编辑字典弹窗 -->
|
<!-- 新增-编辑字典弹窗 -->
|
||||||
@@ -106,7 +84,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import CommonTable from "@/components/proTable/index.vue";
|
import { h } from "vue";
|
||||||
|
import CommonTable from "@/components/proTable/proTablev2.vue";
|
||||||
import dictFieldConfig from "./dictFieldConfig.vue";
|
import dictFieldConfig from "./dictFieldConfig.vue";
|
||||||
import dictManageModules from "./dictManage.vue";
|
import dictManageModules from "./dictManage.vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
@@ -119,13 +98,16 @@ import {
|
|||||||
import { DictManage } from "@/dict";
|
import { DictManage } from "@/dict";
|
||||||
import { formatIndex } from "@/utils/utils";
|
import { formatIndex } from "@/utils/utils";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
|
import { useTableAction } from "@/hooks/useTableAction";
|
||||||
|
|
||||||
import { useDict } from '@/hooks/useDictData';
|
import { useDict } from "@/hooks/useDictData";
|
||||||
const { dicts,refresh } = useDict('permission_list_enable_disable');
|
import { render } from "vue";
|
||||||
|
const { dicts, refresh } = useDict("permission_list_enable_disable");
|
||||||
|
|
||||||
defineOptions({ name: "Dictionary" });
|
defineOptions({ name: "Dictionary" });
|
||||||
const fieldsConfigRef = ref(null);
|
const fieldsConfigRef = ref(null);
|
||||||
const dictTableRef = ref(null);
|
const dictTableRef = ref(null);
|
||||||
|
const { handleAction, handleDelete: runDelete } = useTableAction(dictTableRef);
|
||||||
const dictVisible = ref<boolean>(false);
|
const dictVisible = ref<boolean>(false);
|
||||||
const searchVal = ref<string>("");
|
const searchVal = ref<string>("");
|
||||||
const total = ref<number>(0);
|
const total = ref<number>(0);
|
||||||
@@ -138,70 +120,100 @@ const selectItem = reactive({});
|
|||||||
|
|
||||||
// 列表columns数据
|
// 列表columns数据
|
||||||
const columns = [
|
const columns = [
|
||||||
{ prop: "id", label: "编号", width: "80", align: "center", slot: "number" },
|
{
|
||||||
{ prop: "name", label: "字典名称", align: "center", slot: "name" },
|
prop: "id",
|
||||||
|
label: "编号",
|
||||||
|
width: "80",
|
||||||
|
align: "center",
|
||||||
|
render: ({ rowData, rowIndex }) => {
|
||||||
|
return h("span", `#${formatIndex(rowIndex)}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "name",
|
||||||
|
label: "字典名称",
|
||||||
|
align: "center",
|
||||||
|
render: ({ rowData }: any) => {
|
||||||
|
return h("span", { style: { fontWeight: "600" } }, rowData.name);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: "key",
|
prop: "key",
|
||||||
label: "编码标识",
|
label: "编码标识",
|
||||||
align: "center",
|
align: "center",
|
||||||
slot: "code",
|
width: 300,
|
||||||
|
render: ({ rowData }: any) => {
|
||||||
|
return h(ElTag, { size: "small", type: "info" }, () => rowData.key);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "status",
|
prop: "status",
|
||||||
label: "状态",
|
label: "状态",
|
||||||
align: "center",
|
align: "center",
|
||||||
slot: "status",
|
width: 150,
|
||||||
|
valueType: "status",
|
||||||
|
options: computed(() => dicts.value.permission_list_enable_disable),
|
||||||
|
onClick: ({ cellValue, rowData }) => {
|
||||||
|
handleDictStatus(rowData);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "remark",
|
prop: "remark",
|
||||||
label: "备注说明",
|
label: "备注说明",
|
||||||
align: "center",
|
align: "center",
|
||||||
showOverflowTooltip: true,
|
valueType: "ellipsis",
|
||||||
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "createTime",
|
prop: "createTime",
|
||||||
label: "创建时间",
|
label: "创建时间",
|
||||||
align: "center",
|
align: "center",
|
||||||
showOverflowTooltip: true,
|
valueType: "date",
|
||||||
formatter: (val) => {
|
format: "YYYY-MM-DD HH:mm",
|
||||||
return val.createTime
|
width: 150,
|
||||||
? dayjs(val.createTime).format("YYYY-MM-DD HH:mm")
|
|
||||||
: "-";
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "updateByName",
|
prop: "updateByName",
|
||||||
label: "最后修改人",
|
label: "最后修改人",
|
||||||
align: "center",
|
align: "center",
|
||||||
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "actions",
|
prop: "actions",
|
||||||
label: "操作",
|
label: "操作",
|
||||||
align: "right",
|
align: "right",
|
||||||
width: "300",
|
width: "300",
|
||||||
actions: [
|
render: ({ rowData }: any) => {
|
||||||
|
return h("div", { class: "space-x-2" }, [
|
||||||
|
h(
|
||||||
|
ElButton,
|
||||||
{
|
{
|
||||||
label: "编辑",
|
|
||||||
type: "primary",
|
|
||||||
link: true,
|
link: true,
|
||||||
permission: ["edit"],
|
|
||||||
onClick: (row) => handleEdit(row),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "字段配置",
|
|
||||||
type: "primary",
|
type: "primary",
|
||||||
link:true,
|
onClick: () => handleEdit(rowData),
|
||||||
permission: ["config"],
|
|
||||||
onClick: (row) => handlefieldsConfig(row),
|
|
||||||
},
|
},
|
||||||
|
() => "编辑"
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
ElButton,
|
||||||
{
|
{
|
||||||
label: "删除",
|
link: true,
|
||||||
|
type: "primary",
|
||||||
|
onClick: () => handlefieldsConfig(rowData),
|
||||||
|
},
|
||||||
|
() => "字段配置"
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
ElButton,
|
||||||
|
{
|
||||||
|
link: true,
|
||||||
type: "danger",
|
type: "danger",
|
||||||
link:true,
|
onClick: () => handleDelete(rowData),
|
||||||
permission: ["delete"],
|
},
|
||||||
onClick: (row) => handleDelete(row),
|
() => "删除"
|
||||||
}
|
),
|
||||||
],
|
]);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -223,7 +235,7 @@ const getTableData = async (params) => {
|
|||||||
const response = await getDictValues({
|
const response = await getDictValues({
|
||||||
...params,
|
...params,
|
||||||
...(searchVal.value && { keyword: searchVal.value }),
|
...(searchVal.value && { keyword: searchVal.value }),
|
||||||
...(filterForm.status && { status: filterForm.status })
|
...(filterForm.status && { status: filterForm.status }),
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -235,7 +247,13 @@ const fetchTableData = () => {
|
|||||||
onConfirmSuccess();
|
onConfirmSuccess();
|
||||||
};
|
};
|
||||||
const clearSelectItem = () => {
|
const clearSelectItem = () => {
|
||||||
Object.assign(selectItem,{id:null,name:'',key:'',status:1,remark:''})
|
Object.assign(selectItem, {
|
||||||
|
id: null,
|
||||||
|
name: "",
|
||||||
|
key: "",
|
||||||
|
status: 1,
|
||||||
|
remark: "",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
// 新增字典信息
|
// 新增字典信息
|
||||||
const addDict = () => {
|
const addDict = () => {
|
||||||
@@ -251,17 +269,27 @@ const handleEdit = (item) => {
|
|||||||
// 启用-禁用事件
|
// 启用-禁用事件
|
||||||
const handleDictStatus = async (row) => {
|
const handleDictStatus = async (row) => {
|
||||||
try {
|
try {
|
||||||
row.status === 1 ? await disableDict(row.id) : await enableDict(row.id);
|
const apiCall = row.status === 1 ? disableDict(row.id) : enableDict(row.id);
|
||||||
ElMessage.success("操作成功");
|
// row.status === 1 ? await disableDict(row.id) : await enableDict(row.id);
|
||||||
onConfirmSuccess();
|
await handleAction(apiCall, row.id, getTableData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("error", error);
|
console.log("error", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 刷新Table数据信息
|
// 刷新Table数据信息
|
||||||
const onConfirmSuccess = () => {
|
const onConfirmSuccess = async () => {
|
||||||
dictTableRef.value && dictTableRef.value.reset();
|
if (selectItem.id) {
|
||||||
|
handleAction(
|
||||||
|
Promise.resolve(true),
|
||||||
|
selectItem.id,
|
||||||
|
getTableData,
|
||||||
|
{ showMsg: false, silentUpdate: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dictTableRef.value?.refresh();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// TODO:字段配置
|
// TODO:字段配置
|
||||||
const handlefieldsConfig = (ite) => {
|
const handlefieldsConfig = (ite) => {
|
||||||
@@ -273,8 +301,8 @@ const handleDelete = async (item) => {
|
|||||||
ElMessageBox.confirm("确定要删除吗?", "提示", { type: "warning" })
|
ElMessageBox.confirm("确定要删除吗?", "提示", { type: "warning" })
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
try {
|
try {
|
||||||
await deleteDictValue(item.id);
|
// await deleteDictValue(item.id);
|
||||||
onConfirmSuccess();
|
await runDelete(item.id, deleteDictValue);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("fetch error", error);
|
console.log("fetch error", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ const stageRoute = {
|
|||||||
|
|
||||||
const businessRoute = {
|
const businessRoute = {
|
||||||
business: "businessManage",
|
business: "businessManage",
|
||||||
"business.customer": "customerManage",
|
"business.customer": "customer",
|
||||||
"business.game_studio":"customerManage",
|
"business.game_studio":"gameStudios",
|
||||||
"business.opportunity":"customerManage",
|
"business.opportunity":"businessOpport",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user