Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8ee7b3263 | |||
| 3f47005f76 |
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* 参数配置 API
|
||||||
|
*
|
||||||
|
* TODO: 等后端接口确定后,替换占位的 URL 和返回类型
|
||||||
|
*/
|
||||||
|
import { get, post, put, del } from '@/utils/request';
|
||||||
|
import type { ParamQueryParams, SysParamVO, PageData, ApiResult } from './types';
|
||||||
|
|
||||||
|
// 类型重导出,方便页面 model 层统一引用
|
||||||
|
export type { SysParamVO, ParamQueryParams, PageData, ApiResult } from './types';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// URL 常量(占位,等后端接口确定后替换)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/** 配置列表分页查询 */
|
||||||
|
const PARAM_LIST = '/admin/sys/param/page';
|
||||||
|
/** 新增配置 */
|
||||||
|
const PARAM_ADD = '/admin/sys/param/add';
|
||||||
|
/** 编辑配置 */
|
||||||
|
const PARAM_UPDATE = '/admin/sys/param/update';
|
||||||
|
/** 删除配置 */
|
||||||
|
const PARAM_DELETE = '/admin/sys/param/delete';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// API 函数(占位)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/** 配置列表分页 */
|
||||||
|
export async function getParamList(
|
||||||
|
params: ParamQueryParams,
|
||||||
|
): Promise<ApiResult<PageData<SysParamVO>>> {
|
||||||
|
return get(PARAM_LIST, params as Record<string, any>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增配置 */
|
||||||
|
export async function addParam(data: Partial<SysParamVO>): Promise<ApiResult<null>> {
|
||||||
|
return post(PARAM_ADD, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑配置 */
|
||||||
|
export async function updateParam(data: Partial<SysParamVO>): Promise<ApiResult<null>> {
|
||||||
|
return put(PARAM_UPDATE, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除配置 */
|
||||||
|
export async function deleteParam(id: number): Promise<ApiResult<null>> {
|
||||||
|
return del(`${PARAM_DELETE}`, { id });
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 参数配置 — 类型定义
|
||||||
|
*
|
||||||
|
* TODO: 等后端接口确定后,根据实际字段补充完整类型
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 配置项列表项 */
|
||||||
|
export interface SysParamVO {
|
||||||
|
/** 配置项 ID */
|
||||||
|
id: number;
|
||||||
|
/** 配置项 key */
|
||||||
|
// paramKey: string;
|
||||||
|
/** 配置项 value */
|
||||||
|
// paramValue: string;
|
||||||
|
/** 备注 */
|
||||||
|
// remark: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
// updateTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 配置列表查询参数 */
|
||||||
|
export interface ParamQueryParams {
|
||||||
|
page?: string;
|
||||||
|
limit?: string;
|
||||||
|
/** 搜索关键字 */
|
||||||
|
// keyword?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分页数据结构 */
|
||||||
|
export interface PageData<T> {
|
||||||
|
total: number;
|
||||||
|
list: T[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** API 统一响应格式 */
|
||||||
|
export interface ApiResult<T> {
|
||||||
|
code: number | string;
|
||||||
|
msg: string;
|
||||||
|
data: T;
|
||||||
|
}
|
||||||
@@ -164,6 +164,15 @@ export const FALLBACK_MENU_NODES: MenuNode[] = [
|
|||||||
icon: 'AiOutlineSafetyCertificate',
|
icon: 'AiOutlineSafetyCertificate',
|
||||||
permission: 'system.roles.view',
|
permission: 'system.roles.view',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'system_params',
|
||||||
|
name: '参数配置',
|
||||||
|
path: 'system/params',
|
||||||
|
component: 'system/params',
|
||||||
|
componentName: 'SystemParams',
|
||||||
|
icon: 'AiOutlineSetting',
|
||||||
|
permission: 'system.params.view',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'system_logs',
|
id: 'system_logs',
|
||||||
name: '操作日志',
|
name: '操作日志',
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export const ACTION_TYPE_OPTIONS = [
|
|||||||
{ value: '2', label: '上架赛事' },
|
{ value: '2', label: '上架赛事' },
|
||||||
{ value: '3', label: '禁用用户' },
|
{ value: '3', label: '禁用用户' },
|
||||||
{ value: '4', label: '启用用户' },
|
{ value: '4', label: '启用用户' },
|
||||||
|
{ value: '7', label: '系统参数' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
/** 操作类型文案映射 */
|
/** 操作类型文案映射 */
|
||||||
@@ -31,6 +32,7 @@ export const ACTION_TYPE_MAP: Record<number, string> = {
|
|||||||
2: '上架赛事',
|
2: '上架赛事',
|
||||||
3: '禁用用户',
|
3: '禁用用户',
|
||||||
4: '启用用户',
|
4: '启用用户',
|
||||||
|
7: '系统参数',
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.paramFormModalMain {
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { defineComponent, ref, reactive, nextTick } from 'vue';
|
||||||
|
import { useEffect } from '@/hooks';
|
||||||
|
import { Modal, Form, Input, Button } from 'ant-design-vue';
|
||||||
|
import { paramCodeRules, paramValueRules, remarkRules } from '../model/form';
|
||||||
|
import type { SysParamsDTO } from '../model/services';
|
||||||
|
import styles from './ParamFormModal.module.less';
|
||||||
|
|
||||||
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
interface ParamFormModalProps {
|
||||||
|
visible: boolean;
|
||||||
|
record: SysParamsDTO | null;
|
||||||
|
submitting?: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onSave: (formData: { paramCode: string; paramValue: string; remark: string }) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 默认表单数据 */
|
||||||
|
const getDefaultForm = () => ({
|
||||||
|
paramCode: '',
|
||||||
|
paramValue: '',
|
||||||
|
remark: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ParamFormModal',
|
||||||
|
props: {
|
||||||
|
visible: { type: Boolean, default: false },
|
||||||
|
record: { type: Object, default: null },
|
||||||
|
submitting: { type: Boolean, default: false },
|
||||||
|
onClose: { type: Function, required: true },
|
||||||
|
onSave: { type: Function, required: true },
|
||||||
|
},
|
||||||
|
setup(props: ParamFormModalProps) {
|
||||||
|
const formRef = ref<any>();
|
||||||
|
const formData = reactive(getDefaultForm());
|
||||||
|
|
||||||
|
/** 根据 record 初始化表单 */
|
||||||
|
const initFormFromRecord = (record: SysParamsDTO | null) => {
|
||||||
|
if (record) {
|
||||||
|
Object.assign(formData, {
|
||||||
|
paramCode: record.paramCode || '',
|
||||||
|
paramValue: record.paramValue || '',
|
||||||
|
remark: record.remark || '',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Object.assign(formData, getDefaultForm());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 监听 visible 变化重置表单 */
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.visible) {
|
||||||
|
initFormFromRecord(props.record);
|
||||||
|
nextTick().then(() => formRef.value?.clearValidate());
|
||||||
|
}
|
||||||
|
}, [() => props.visible]);
|
||||||
|
|
||||||
|
/** 提交 */
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
await formRef.value?.validate();
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
props.onSave({
|
||||||
|
paramCode: formData.paramCode,
|
||||||
|
paramValue: formData.paramValue,
|
||||||
|
remark: formData.remark,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 关闭 */
|
||||||
|
const handleClose = () => {
|
||||||
|
props.onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const isEdit = !!props.record;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={isEdit ? '编辑配置' : '新增配置'}
|
||||||
|
visible={props.visible}
|
||||||
|
onCancel={handleClose}
|
||||||
|
width={520}
|
||||||
|
destroyOnClose
|
||||||
|
footer={null}
|
||||||
|
centered
|
||||||
|
wrapClassName={styles.paramFormModalMain}
|
||||||
|
>
|
||||||
|
<Form ref={formRef} layout="vertical" model={formData} requiredMark>
|
||||||
|
<Form.Item label="参数编码" name="paramCode" rules={paramCodeRules}>
|
||||||
|
<Input
|
||||||
|
value={formData.paramCode}
|
||||||
|
onUpdate:value={(val: any) => {
|
||||||
|
formData.paramCode = val?.target?.value ?? val ?? '';
|
||||||
|
}}
|
||||||
|
placeholder="请输入参数编码"
|
||||||
|
allowClear
|
||||||
|
readonly={isEdit}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="参数值" name="paramValue" rules={paramValueRules}>
|
||||||
|
<Input
|
||||||
|
value={formData.paramValue}
|
||||||
|
onUpdate:value={(val: any) => {
|
||||||
|
formData.paramValue = val?.target?.value ?? val ?? '';
|
||||||
|
}}
|
||||||
|
placeholder="请输入参数值"
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="备注说明" name="remark" rules={remarkRules}>
|
||||||
|
<TextArea
|
||||||
|
value={formData.remark}
|
||||||
|
onUpdate:value={(val: any) => {
|
||||||
|
formData.remark = val?.target?.value ?? val ?? '';
|
||||||
|
}}
|
||||||
|
placeholder="请输入备注说明(选填,最多200字)"
|
||||||
|
maxlength={200}
|
||||||
|
showCount
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
{/* 底部按钮 */}
|
||||||
|
<div class={styles.footer}>
|
||||||
|
<Button onClick={handleClose}>取消</Button>
|
||||||
|
<Button type="primary" loading={props.submitting} onClick={handleSubmit}>
|
||||||
|
确认保存
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// 参数值单元格:文字最多 2 行省略,末尾带"查看"链接
|
||||||
|
:global {
|
||||||
|
.param-value-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
width: 100%;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-value-text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1.5;
|
||||||
|
word-break: break-all;
|
||||||
|
color: rgba(0, 0, 0, 0.88);
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-value-view {
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #1677ff;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
import { defineComponent, ref, nextTick } from 'vue';
|
||||||
|
import { Button, Input, Table, Form, Space, Modal, Pagination, message } from 'ant-design-vue';
|
||||||
|
import { useParamsModel } from './model/useParamsModel';
|
||||||
|
import { saveParam, updateParam } from './model/services';
|
||||||
|
import { useState, useThrottleFn, useContainerSize, useEffect } from '@/hooks';
|
||||||
|
import ParamFormModal from './components/ParamFormModal';
|
||||||
|
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
||||||
|
import './index.module.less';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数值单元格:最多 2 行截断,溢出时显示"查看"按钮
|
||||||
|
*/
|
||||||
|
const ParamValueCell = defineComponent({
|
||||||
|
name: 'ParamValueCell',
|
||||||
|
props: { text: { type: String, required: true } },
|
||||||
|
setup(props) {
|
||||||
|
const textRef = ref<HTMLElement | null>(null);
|
||||||
|
const [overflow, setOverflow] = useState(false);
|
||||||
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const checkOverflow = () => {
|
||||||
|
const el = textRef.value;
|
||||||
|
if (el) setOverflow(el.scrollHeight > el.clientHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
let observer: ResizeObserver | null = null;
|
||||||
|
useEffect(() => {
|
||||||
|
if (!textRef.value) return;
|
||||||
|
nextTick(checkOverflow);
|
||||||
|
observer = new ResizeObserver(checkOverflow);
|
||||||
|
observer.observe(textRef.value);
|
||||||
|
return () => {
|
||||||
|
observer?.disconnect();
|
||||||
|
observer = null;
|
||||||
|
};
|
||||||
|
}, [() => textRef.value]);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const raw = props.text;
|
||||||
|
return (
|
||||||
|
<div class="param-value-cell">
|
||||||
|
<div ref={textRef} class="param-value-text">
|
||||||
|
{raw}
|
||||||
|
</div>
|
||||||
|
{overflow.value ? (
|
||||||
|
<span class="param-value-view" onClick={() => setModalOpen(true)}>
|
||||||
|
查看
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
<Modal
|
||||||
|
visible={modalOpen.value}
|
||||||
|
title="参数值"
|
||||||
|
footer={null}
|
||||||
|
width={600}
|
||||||
|
centered
|
||||||
|
destroyOnClose
|
||||||
|
onCancel={() => setModalOpen(false)}
|
||||||
|
>
|
||||||
|
<div style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-all', lineHeight: 1.8 }}>
|
||||||
|
{raw}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bodyCell 渲染
|
||||||
|
*/
|
||||||
|
function renderBodyCell({
|
||||||
|
column,
|
||||||
|
text,
|
||||||
|
record,
|
||||||
|
onEdit,
|
||||||
|
}: {
|
||||||
|
column: any;
|
||||||
|
text: any;
|
||||||
|
record: any;
|
||||||
|
onEdit: (record: any) => void;
|
||||||
|
}) {
|
||||||
|
// 操作列
|
||||||
|
if (column.key === 'action') {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<Button type="link" size="small" onClick={() => onEdit(record)}>
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 参数值列:两行截断 + 查看弹窗
|
||||||
|
if (column.dataIndex === 'paramValue') {
|
||||||
|
const raw = text || '';
|
||||||
|
return raw ? <ParamValueCell text={raw} /> : <span>--</span>;
|
||||||
|
}
|
||||||
|
// 备注列:空值显示 --
|
||||||
|
if (column.dataIndex === 'remark' && !text) {
|
||||||
|
return <span>--</span>;
|
||||||
|
}
|
||||||
|
return <span>{text ?? '--'}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统管理 - 配置管理
|
||||||
|
*/
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'SystemParams',
|
||||||
|
setup() {
|
||||||
|
const {
|
||||||
|
filterForm,
|
||||||
|
loading,
|
||||||
|
dataSource,
|
||||||
|
columns,
|
||||||
|
pagination,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
} = useParamsModel();
|
||||||
|
|
||||||
|
const { containerRef, height } = useContainerSize({ headerOffset: 55 });
|
||||||
|
|
||||||
|
// ===== 弹窗状态 =====
|
||||||
|
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||||
|
const [editingRecord, setEditingRecord] = useState<any>(null);
|
||||||
|
const [formSubmitting, setFormSubmitting] = useState<boolean>(false);
|
||||||
|
|
||||||
|
// ===== 新增 =====
|
||||||
|
const handleAdd = () => {
|
||||||
|
setEditingRecord(null);
|
||||||
|
setModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 编辑 =====
|
||||||
|
const handleEdit = (record: any) => {
|
||||||
|
setEditingRecord(record);
|
||||||
|
setModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 关闭弹窗 =====
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setModalVisible(false);
|
||||||
|
setEditingRecord(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 保存(新增/编辑) =====
|
||||||
|
const handleSave = useThrottleFn(async (formPayload: any) => {
|
||||||
|
if (formSubmitting.value) return;
|
||||||
|
setFormSubmitting(true);
|
||||||
|
try {
|
||||||
|
let res: any;
|
||||||
|
if (editingRecord.value) {
|
||||||
|
res = await updateParam({
|
||||||
|
id: editingRecord.value.id,
|
||||||
|
paramCode: formPayload.paramCode,
|
||||||
|
paramValue: formPayload.paramValue,
|
||||||
|
remark: formPayload.remark,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res = await saveParam({
|
||||||
|
paramCode: formPayload.paramCode,
|
||||||
|
paramValue: formPayload.paramValue,
|
||||||
|
remark: formPayload.remark,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (res.code == 200) {
|
||||||
|
message.success(editingRecord.value ? '编辑成功' : '新增成功');
|
||||||
|
handleCloseModal();
|
||||||
|
handleSearch();
|
||||||
|
} else {
|
||||||
|
message.error(res.msg || '操作失败');
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('保存配置失败:', error);
|
||||||
|
} finally {
|
||||||
|
setFormSubmitting(false);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// ===== 表格最终列(含操作列) =====
|
||||||
|
const tableColumns = [
|
||||||
|
...columns,
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
width: 100,
|
||||||
|
fixed: 'right' as const,
|
||||||
|
align: 'center' as const,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<div class={pageStyles.containerMain}>
|
||||||
|
{/* ===== 筛选区 ===== */}
|
||||||
|
<div class={pageStyles.filter}>
|
||||||
|
<Form layout="inline" model={filterForm}>
|
||||||
|
<Form.Item label="参数编码" name="paramCode">
|
||||||
|
<Input
|
||||||
|
value={filterForm.paramCode}
|
||||||
|
placeholder="请输入参数编码"
|
||||||
|
style={{ width: '200px' }}
|
||||||
|
allowClear
|
||||||
|
onUpdate:value={(val: any) =>
|
||||||
|
(filterForm.paramCode = val?.target?.value ?? val ?? '')
|
||||||
|
}
|
||||||
|
onPressEnter={handleSearch}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={handleReset}>重置</Button>
|
||||||
|
<Button type="primary" onClick={handleSearch} loading={loading.value}>
|
||||||
|
查询
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" onClick={handleAdd}>
|
||||||
|
新增
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ===== 表格区 ===== */}
|
||||||
|
<div class={pageStyles.table}>
|
||||||
|
<div ref={containerRef} class={pageStyles.tableBody}>
|
||||||
|
<Table
|
||||||
|
columns={tableColumns}
|
||||||
|
dataSource={dataSource.value}
|
||||||
|
loading={loading.value}
|
||||||
|
scroll={{ x: 'max-content', y: height.value }}
|
||||||
|
pagination={false}
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
bodyCell: (args: any) =>
|
||||||
|
renderBodyCell({
|
||||||
|
...args,
|
||||||
|
onEdit: handleEdit,
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 独立分页 */}
|
||||||
|
<div class={pageStyles.pagination}>
|
||||||
|
<Pagination
|
||||||
|
current={(pagination as any).current.value}
|
||||||
|
pageSize={(pagination as any).pageSize.value}
|
||||||
|
total={(pagination as any).total.value}
|
||||||
|
showSizeChanger
|
||||||
|
showTotal={(total: number) => `共 ${total} 条`}
|
||||||
|
onChange={handlePageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ===== 新增/编辑弹窗 ===== */}
|
||||||
|
{modalVisible.value && (
|
||||||
|
<ParamFormModal
|
||||||
|
visible={modalVisible.value}
|
||||||
|
record={editingRecord.value}
|
||||||
|
submitting={formSubmitting.value}
|
||||||
|
onClose={handleCloseModal}
|
||||||
|
onSave={handleSave}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 参数配置 - 常量配置
|
||||||
|
*
|
||||||
|
* 集中放置该页面的下拉选项、字段映射、状态文案等常量
|
||||||
|
*/
|
||||||
|
import type { SelectOption } from '@/types';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 字段映射(表格列与 API 字段对齐)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/** API 字段 → 表格列中文映射 */
|
||||||
|
export const FIELD_MAPPINGS: Record<string, string> = {
|
||||||
|
// TODO: 等后端接口确定后,根据实际字段补充
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 下拉选项
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/** 示例:状态筛选选项 */
|
||||||
|
export const STATUS_OPTIONS: SelectOption[] = [{ value: '', label: '全部' }];
|
||||||
|
|
||||||
|
/** 示例:每页条数选项 */
|
||||||
|
export const PAGE_SIZE_OPTIONS: SelectOption[] = [
|
||||||
|
{ value: 10, label: '10条/页' },
|
||||||
|
{ value: 20, label: '20条/页' },
|
||||||
|
{ value: 50, label: '50条/页' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* 参数配置 - 表单校验规则
|
||||||
|
*
|
||||||
|
* 集中管理新增/编辑弹窗的表单校验规则。
|
||||||
|
*/
|
||||||
|
import type { Rule } from 'ant-design-vue/es/form';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 校验规则
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/** 参数编码:必填 */
|
||||||
|
export const paramCodeRules: Rule[] = [{ required: true, message: '请输入参数编码' }];
|
||||||
|
|
||||||
|
/** 参数值:必填 */
|
||||||
|
export const paramValueRules: Rule[] = [{ required: true, message: '请输入参数值' }];
|
||||||
|
|
||||||
|
/** 备注说明:选填,最多 200 字 */
|
||||||
|
export const remarkRules: Rule[] = [{ max: 200, message: '备注说明最多 200 个字' }];
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* 参数配置 - 接口服务
|
||||||
|
*
|
||||||
|
* 该页面使用的所有接口集中管理,供 useModel Hook 调用。
|
||||||
|
*/
|
||||||
|
import { get, post } from '@/utils/request';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 类型定义
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/** 配置项 VO */
|
||||||
|
export interface SysParamsDTO {
|
||||||
|
id: string;
|
||||||
|
paramCode: string;
|
||||||
|
paramValue: string;
|
||||||
|
remark: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分页查询参数 */
|
||||||
|
export interface ParamQueryParams {
|
||||||
|
page: string;
|
||||||
|
limit: string;
|
||||||
|
paramCode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增参数 */
|
||||||
|
export interface ParamSaveParams {
|
||||||
|
paramCode: string;
|
||||||
|
paramValue: string;
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑参数 */
|
||||||
|
export interface ParamUpdateParams {
|
||||||
|
id: string;
|
||||||
|
paramCode: string;
|
||||||
|
paramValue: string;
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分页数据结构 */
|
||||||
|
export interface PageData<T> {
|
||||||
|
total: number;
|
||||||
|
list: T[];
|
||||||
|
exData?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 接口通用返回结构 */
|
||||||
|
export interface ApiResult<T> {
|
||||||
|
code: number;
|
||||||
|
msg: string;
|
||||||
|
data: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// URL 常量
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
const PARAM_PAGE = '/sys/params/page';
|
||||||
|
const PARAM_SAVE = '/sys/params/save';
|
||||||
|
const PARAM_UPDATE = '/sys/params/update';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// API 函数
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/** 配置列表分页查询 */
|
||||||
|
export function getParamList(params: ParamQueryParams): Promise<ApiResult<PageData<SysParamsDTO>>> {
|
||||||
|
return get(PARAM_PAGE, params as Record<string, any>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增配置 */
|
||||||
|
export function saveParam(params: ParamSaveParams): Promise<ApiResult<null>> {
|
||||||
|
return post(PARAM_SAVE, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑配置 */
|
||||||
|
export function updateParam(params: ParamUpdateParams): Promise<ApiResult<null>> {
|
||||||
|
return post(PARAM_UPDATE, params);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 参数配置 - 表格列配置(视图层)
|
||||||
|
* 纯展示逻辑,不包含状态管理
|
||||||
|
*/
|
||||||
|
import type { SysParamsDTO } from './services';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数配置表格列定义
|
||||||
|
*
|
||||||
|
* 列:参数编码 / 参数值 / 备注说明
|
||||||
|
* 操作列在 index.tsx 中动态追加
|
||||||
|
*/
|
||||||
|
export function useParamsColumns() {
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '参数编码',
|
||||||
|
dataIndex: 'paramCode',
|
||||||
|
key: 'paramCode',
|
||||||
|
width: 230,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '参数值',
|
||||||
|
dataIndex: 'paramValue',
|
||||||
|
key: 'paramValue',
|
||||||
|
width: 480,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '备注说明',
|
||||||
|
dataIndex: 'remark',
|
||||||
|
key: 'remark',
|
||||||
|
minWidth: 240,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return { columns };
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { computed, reactive, toRef, nextTick, type Ref } from 'vue';
|
||||||
|
import { useThrottleFn, useDebounce } from '@/hooks';
|
||||||
|
import { useRequest } from '@/hooks/useRequest';
|
||||||
|
import { useEffect } from '@/hooks/useEffect';
|
||||||
|
import { usePagination, syncPaginationTotal } from '@/hooks/usePagination';
|
||||||
|
import {
|
||||||
|
getParamList,
|
||||||
|
type SysParamsDTO,
|
||||||
|
type ParamQueryParams,
|
||||||
|
type PageData,
|
||||||
|
type ApiResult,
|
||||||
|
} from './services';
|
||||||
|
import { useParamsColumns } from './useParamsColumns';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Model(业务逻辑 Hook)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数配置页数据模型
|
||||||
|
*/
|
||||||
|
export function useParamsModel() {
|
||||||
|
// ===== 筛选条件 =====
|
||||||
|
const filterForm = reactive({
|
||||||
|
paramCode: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 参数编码筛选防抖 300ms
|
||||||
|
const { debouncedValue: debouncedParamCode } = useDebounce(
|
||||||
|
toRef(filterForm, 'paramCode') as Ref<string>,
|
||||||
|
{ delay: 300 },
|
||||||
|
);
|
||||||
|
|
||||||
|
// ===== 分页 =====
|
||||||
|
const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 });
|
||||||
|
|
||||||
|
// ===== 构建 API 查询参数 =====
|
||||||
|
const buildQueryParams = (): ParamQueryParams => {
|
||||||
|
const params: ParamQueryParams = {
|
||||||
|
page: String((pagination as any).current.value),
|
||||||
|
limit: String((pagination as any).pageSize.value),
|
||||||
|
};
|
||||||
|
if (filterForm.paramCode.trim()) {
|
||||||
|
params.paramCode = filterForm.paramCode.trim();
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 数据请求 =====
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
run: fetchList,
|
||||||
|
} = useRequest<ApiResult<PageData<SysParamsDTO>>>(() => getParamList(buildQueryParams()), {
|
||||||
|
refreshDeps: [],
|
||||||
|
formatResult: (res) => res,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dataSource = computed(() => {
|
||||||
|
const res = data.value;
|
||||||
|
return res ? (res as any).data?.list || [] : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
syncPaginationTotal(pagination, (data.value as any)?.data?.total);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
// ===== 表格列配置 =====
|
||||||
|
const { columns } = useParamsColumns();
|
||||||
|
|
||||||
|
// ===== 计算属性 =====
|
||||||
|
const hasFilter = computed(() => {
|
||||||
|
return debouncedParamCode.value.trim() !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== 方法 =====
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
pagination.reset();
|
||||||
|
fetchList();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 重置(重置后自动查询) */
|
||||||
|
const handleReset = useThrottleFn(async () => {
|
||||||
|
filterForm.paramCode = '';
|
||||||
|
pagination.reset();
|
||||||
|
await fetchList();
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
/** 分页变更 */
|
||||||
|
const handlePageChange = (page: number, pageSize: number) => {
|
||||||
|
pagination.setCurrent(page);
|
||||||
|
if (pageSize !== (pagination as any).pageSize.value) {
|
||||||
|
pagination.setPageSize(pageSize);
|
||||||
|
}
|
||||||
|
nextTick().then(() => fetchList());
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
filterForm,
|
||||||
|
loading,
|
||||||
|
dataSource,
|
||||||
|
columns,
|
||||||
|
pagination,
|
||||||
|
hasFilter,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
fetchList,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user