配置更新

This commit is contained in:
aaron 2025-02-26 20:42:59 +08:00
parent 366b97d958
commit 8257301dd2
4 changed files with 430 additions and 1 deletions

View File

@ -106,6 +106,9 @@
<a-menu-item key="system-logs">
<router-link to="/system/logs">日志查询</router-link>
</a-menu-item>
<a-menu-item key="system-config">
<router-link to="/system/config">系统配置</router-link>
</a-menu-item>
</a-sub-menu>
</a-menu>
</a-layout-sider>
@ -325,6 +328,11 @@ export default defineComponent({
key: 'system-logs',
title: '日志查询',
path: '/system/logs'
},
{
key: 'system-config',
title: '系统配置',
path: '/system/config'
}
]
},

View File

@ -94,6 +94,11 @@ const routes = [
path: 'logs',
component: () => import('@/views/system/LogList.vue'),
meta: { title: '日志查询' }
},
{
path: 'config',
component: () => import('@/views/system/ConfigList.vue'),
meta: { title: '系统配置' }
}
]
},

View File

@ -144,6 +144,19 @@
</a-col>
</a-row>
<a-form-item
label="总限领数量"
name="total_limit"
extra="不填写表示不限制总领取次数"
>
<a-input-number
v-model:value="formState.total_limit"
:min="0"
style="width: 100%"
placeholder="请输入总限领数量"
/>
</a-form-item>
<a-form-item
label="每日限领数量"
name="daily_limit"
@ -277,6 +290,16 @@ export default defineComponent({
width: 100,
align: 'center'
},
{
title: '总限领',
dataIndex: 'total_limit',
key: 'total_limit',
width: 100,
align: 'center',
customRender: ({ text }) => {
return text || '不限'
}
},
{
title: '已领取次数',
dataIndex: 'receive_count',
@ -352,6 +375,7 @@ export default defineComponent({
end_time: null,
daily_start_time: null,
daily_end_time: null,
total_limit: undefined,
daily_limit: 1,
coupon_config: {},
is_active: true
@ -400,6 +424,7 @@ export default defineComponent({
end_time: null,
daily_start_time: null,
daily_end_time: null,
total_limit: undefined,
daily_limit: 1,
coupon_config: {},
is_active: true
@ -417,6 +442,14 @@ export default defineComponent({
//
await fetchCouponTemplates()
// 0
const couponConfig = {}
Object.entries(record.coupon_config || {}).forEach(([key, value]) => {
if (value && value > 0) {
couponConfig[key] = value
}
})
//
formState.value = {
name: record.name,
@ -425,8 +458,9 @@ export default defineComponent({
end_time: dayjs(record.end_time),
daily_start_time: dayjs(record.daily_start_time, 'HH:mm:ss'),
daily_end_time: dayjs(record.daily_end_time, 'HH:mm:ss'),
total_limit: record.total_limit,
daily_limit: record.daily_limit,
coupon_config: record.coupon_config || {},
coupon_config: couponConfig,
is_active: record.is_active
}
@ -442,8 +476,17 @@ export default defineComponent({
formRef.value.validate().then(async () => {
try {
submitting.value = true
//
const couponConfig = {}
Object.entries(formState.value.coupon_config).forEach(([key, value]) => {
if (value && value > 0) {
couponConfig[key] = value
}
})
const params = {
...formState.value,
coupon_config: couponConfig,
start_time: dayjs(formState.value.start_time).format('YYYY-MM-DD HH:mm:ss'),
end_time: dayjs(formState.value.end_time).format('YYYY-MM-DD HH:mm:ss'),
daily_start_time: dayjs(formState.value.daily_start_time).format('HH:mm:ss'),

View File

@ -0,0 +1,373 @@
<template>
<page-container>
<div class="config-list">
<div class="table-header">
<h1>系统配置</h1>
<a-button type="primary" @click="showAddModal">
新增配置
</a-button>
</div>
<a-table
:columns="columns"
:data-source="tableData"
:pagination="pagination"
:loading="loading"
@change="handleTableChange"
row-key="id"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'value'">
<span>{{ formatValue(record) }}</span>
</template>
<template v-if="column.key === 'create_time'">
{{ formatDateTime(record.create_time) }}
</template>
<template v-if="column.key === 'update_time'">
{{ formatDateTime(record.update_time) }}
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="handleEdit(record)">编辑</a>
<a-popconfirm
title="确定要删除这个配置吗?"
@confirm="handleDelete(record)"
>
<a class="danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
<!-- 添加/编辑配置模态框 -->
<a-modal
v-model:visible="modalVisible"
:title="isEdit ? '编辑配置' : '新增配置'"
@ok="handleSubmit"
@cancel="handleCancel"
:confirmLoading="submitting"
>
<a-form
ref="formRef"
:model="formState"
:rules="rules"
layout="vertical"
>
<a-form-item
label="配置键名"
name="key"
:rules="[{ required: true, message: '请输入配置键名' }]"
>
<a-input
v-model:value="formState.key"
placeholder="请输入配置键名"
:disabled="isEdit"
:maxLength="50"
/>
</a-form-item>
<a-form-item
label="配置值"
name="value"
:rules="[{ required: true, message: '请输入配置值' }]"
>
<a-input
v-model:value="formState.value"
placeholder="请输入配置值"
/>
</a-form-item>
<a-form-item
label="备注"
name="description"
>
<a-textarea
v-model:value="formState.description"
placeholder="请输入备注信息"
:rows="2"
:maxLength="200"
/>
</a-form-item>
</a-form>
</a-modal>
</div>
</page-container>
</template>
<script>
import { defineComponent, ref, onMounted, watch } from 'vue'
import {
message,
Input,
Select,
Switch,
InputNumber
} from 'ant-design-vue'
import PageContainer from '@/components/PageContainer.vue'
import dayjs from 'dayjs'
import request from '@/utils/request'
export default defineComponent({
components: {
PageContainer,
AInput: Input,
ATextarea: Input.TextArea,
ASelect: Select,
ASelectOption: Select.Option,
ASwitch: Switch,
AInputNumber: InputNumber
},
setup() {
const loading = ref(false)
const tableData = ref([])
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showTotal: (total) => `${total} 条记录`
})
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 60,
align: 'center'
},
{
title: '配置键名',
dataIndex: 'key',
key: 'key',
width: 150
},
{
title: '配置值',
key: 'value',
width: 200,
ellipsis: true
},
{
title: '备注',
dataIndex: 'description',
key: 'description',
width: 200,
ellipsis: true
},
{
title: '操作',
key: 'action',
width: 120,
fixed: 'right'
}
]
//
const formatDateTime = (value) => {
if (!value) return '-'
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
}
//
const formatValue = (record) => {
if (!record.value) return '-'
if (record.type === 'boolean') {
return record.value === true || record.value === 'true' ? '是' : '否'
} else if (record.type === 'json') {
try {
const obj = JSON.parse(record.value)
return JSON.stringify(obj, null, 2)
} catch (e) {
return record.value
}
}
return record.value
}
//
const fetchData = async () => {
try {
loading.value = true
const res = await request.get('/api/config')
if (res.code === 200) {
tableData.value = res.data
pagination.value.total = res.data.length
} else {
message.error(res.message || '获取数据失败')
}
} catch (error) {
console.error('获取系统配置列表失败:', error)
message.error('获取数据失败')
} finally {
loading.value = false
}
}
//
const handleTableChange = (pag) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
fetchData()
}
const modalVisible = ref(false)
const submitting = ref(false)
const isEdit = ref(false)
const currentId = ref(null)
const formRef = ref(null)
const formState = ref({
key: '',
value: '',
description: ''
})
const rules = {
key: [
{ required: true, message: '请输入配置键名' },
{ pattern: /^[a-zA-Z0-9_\.]+$/, message: '键名只能包含字母、数字、下划线和点' }
],
value: [
{ required: true, message: '请输入配置值' },
]
}
//
const showAddModal = () => {
isEdit.value = false
currentId.value = null
formState.value = {
key: '',
value: '',
description: ''
}
modalVisible.value = true
}
//
const handleEdit = (record) => {
isEdit.value = true
currentId.value = record.id
formState.value = {
key: record.key,
value: record.value,
description: record.description || ''
}
modalVisible.value = true
}
//
const handleSubmit = () => {
formRef.value.validate().then(async () => {
try {
submitting.value = true
const params = {
key: formState.value.key,
value: formState.value.value,
description: formState.value.description
}
let res
if (isEdit.value) {
res = await request.put(`/api/config/${formState.value.key}`, params)
} else {
res = await request.post('/api/config', params)
}
if (res.code === 200) {
message.success(isEdit.value ? '更新成功' : '创建成功')
modalVisible.value = false
fetchData()
} else {
message.error(res.message || (isEdit.value ? '更新失败' : '创建失败'))
}
} catch (error) {
console.error(isEdit.value ? '更新系统配置失败:' : '创建系统配置失败:', error)
message.error(isEdit.value ? '更新失败' : '创建失败')
} finally {
submitting.value = false
}
})
}
//
const handleCancel = () => {
formRef.value?.resetFields()
modalVisible.value = false
}
//
const handleDelete = async (record) => {
try {
loading.value = true
const res = await request.delete(`/api/config/${record.key}`)
if (res.code === 200) {
message.success('删除成功')
fetchData()
} else {
message.error(res.message || '删除失败')
}
} catch (error) {
console.error('删除系统配置失败:', error)
message.error('删除失败')
} finally {
loading.value = false
}
}
onMounted(() => {
fetchData()
})
return {
loading,
tableData,
columns,
pagination,
formatValue,
handleTableChange,
modalVisible,
submitting,
formState,
formRef,
rules,
showAddModal,
handleEdit,
handleSubmit,
handleCancel,
isEdit,
handleDelete
}
}
})
</script>
<style scoped>
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.table-header h1 {
margin: 0;
}
.danger {
color: #ff4d4f;
}
:deep(.ant-table-content) {
overflow-x: auto;
}
</style>