初始化

This commit is contained in:
2026-03-03 09:53:54 +08:00
commit 3f349a35a4
437 changed files with 65639 additions and 0 deletions

View File

@@ -0,0 +1,228 @@
<template>
<el-dialog
v-model="visible"
:title="dialogType === 'add' ? '新增配置' : '编辑配置'"
width="600px"
align-center
:close-on-click-modal="false"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
<el-form-item label="配置标识" prop="key">
<el-input v-model="formData.key" placeholder="请输入配置标识" />
</el-form-item>
<el-form-item label="配置标题" prop="name">
<el-input v-model="formData.name" placeholder="请输入配置标题" />
</el-form-item>
<el-form-item label="组件类型" prop="input_type">
<el-select v-model="formData.input_type" :options="inputComponent" />
</el-form-item>
<el-form-item
label="组件数据"
prop="config_select_data"
v-if="['select', 'radio'].includes(formData.input_type)"
>
<el-row
:gutter="10"
class="mb-2"
v-for="(item, index) in formData.config_select_data"
:key="index"
>
<el-col :span="10">
<el-input v-model="item.label" placeholder="请输入label"></el-input>
</el-col>
<el-col :span="10">
<el-input v-model="item.value" placeholder="请输入value"></el-input>
</el-col>
<el-col :span="4">
<el-button type="danger" @click="removeConfigSelectData(index)">
<template #icon>
<ArtSvgIcon icon="ri:delete-bin-5-line" />
</template>
</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-button type="primary" @click="addConfigSelectData">
<template #icon>
<ArtSvgIcon icon="ri:add-fill" />
</template>
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="formData.sort" placeholder="请输入排序" />
</el-form-item>
<el-form-item label="描述" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
:rows="3"
placeholder="请输入配置描述"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import api from '@/api/system/config'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
interface Props {
modelValue: boolean
dialogType: string
data?: Record<string, any>
}
interface Emits {
(e: 'update:modelValue', value: boolean): void
(e: 'success'): void
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
dialogType: 'add',
data: undefined
})
const emit = defineEmits<Emits>()
const formRef = ref<FormInstance>()
interface ConfigSelectData {
label: string
value: string
}
const inputComponent = [
{ label: '文本框', value: 'input' },
{ label: '文本域', value: 'textarea' },
{ label: '下拉选择框', value: 'select' },
{ label: '单选框', value: 'radio' },
{ label: '图片上传', value: 'uploadImage' },
{ label: '文件上传', value: 'uploadFile' },
{ label: '富文本编辑器', value: 'wangEditor' }
]
const addConfigSelectData = () => {
formData.config_select_data.push({ label: '', value: '' })
}
const removeConfigSelectData = (index: number) => {
formData.config_select_data.splice(index, 1)
}
/**
* 弹窗显示状态双向绑定
*/
const visible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
/**
* 表单验证规则
*/
const rules = reactive<FormRules>({
key: [{ required: true, message: '请输入配置标识', trigger: 'blur' }],
name: [{ required: true, message: '请输入配置标题', trigger: 'blur' }],
input_type: [{ required: true, message: '请输入组件类型', trigger: 'blur' }]
})
/**
* 初始数据
*/
const initialFormData = {
id: null,
group_id: null,
key: '',
value: '',
name: '',
input_type: 'input',
config_select_data: [] as ConfigSelectData[],
sort: 100,
remark: ''
}
/**
* 表单数据
*/
const formData = reactive({ ...initialFormData })
/**
* 监听弹窗打开,初始化表单数据
*/
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
initPage()
}
}
)
/**
* 初始化页面数据
*/
const initPage = async () => {
// 先重置为初始值
Object.assign(formData, initialFormData)
formData.config_select_data = []
// 如果有数据,则填充数据
if (props.data) {
await nextTick()
initForm()
}
}
/**
* 初始化表单数据
*/
const initForm = () => {
if (props.data) {
for (const key in formData) {
if (props.data[key] != null && props.data[key] != undefined) {
;(formData as any)[key] = props.data[key]
}
}
}
}
/**
* 关闭弹窗并重置表单
*/
const handleClose = () => {
visible.value = false
formRef.value?.resetFields()
}
/**
* 提交表单
*/
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
if (props.dialogType === 'add') {
await api.configSave(formData)
ElMessage.success('新增成功')
} else {
await api.configUpdate(formData)
ElMessage.success('修改成功')
}
emit('success')
handleClose()
} catch (error) {
console.log('表单验证失败:', error)
}
}
</script>

View File

@@ -0,0 +1,192 @@
<template>
<el-drawer
v-model="visible"
title="配置管理"
size="70%"
destroy-on-close
:close-on-click-modal="false"
@close="handleClose"
>
<div class="art-full-height">
<!-- 表格头部 -->
<div>
<ElSpace wrap>
<ElButton v-permission="'core:config:edit'" @click="handleAddClick" v-ripple>
<template #icon>
<ArtSvgIcon icon="ri:add-fill" />
</template>
新增
</ElButton>
<ElButton
v-permission="'core:config:edit'"
:disabled="selectedRows.length === 0"
@click="deleteSelectedRows(api.configDelete, refreshData)"
v-ripple
>
<template #icon>
<ArtSvgIcon icon="ri:delete-bin-5-line" />
</template>
删除
</ElButton>
</ElSpace>
</div>
<!-- 表格 -->
<ArtTable
ref="tableRef"
rowKey="id"
:loading="loading"
:data="tableData"
:columns="columns"
:pagination="pagination"
@sort-change="handleSortChange"
@selection-change="handleSelectionChange"
@pagination:size-change="handleSizeChange"
@pagination:current-change="handleCurrentChange"
>
<!-- 操作列 -->
<template #operation="{ row }">
<div class="flex gap-2">
<SaButton
v-permission="'core:config:edit'"
type="secondary"
@click="showDialog('edit', row)"
/>
<SaButton
v-permission="'core:config:edit'"
type="error"
@click="deleteRow(row, api.configDelete, refreshData)"
/>
</div>
</template>
</ArtTable>
<ConfigEditDialog
v-model="dialogVisible"
:dialog-type="dialogType"
:data="dialogData"
@success="refreshData"
/>
</div>
</el-drawer>
</template>
<script setup lang="ts">
import api from '@/api/system/config'
import { useTable } from '@/hooks/core/useTable'
import { useSaiAdmin } from '@/composables/useSaiAdmin'
import ConfigEditDialog from './config-edit-dialog.vue'
interface Props {
modelValue: boolean
data?: Record<string, any>
}
interface Emits {
(e: 'update:modelValue', value: boolean): void
(e: 'success'): void
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
data: undefined
})
const emit = defineEmits<Emits>()
/**
* 弹窗显示状态双向绑定
*/
const visible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
/**
* 监听弹窗打开,初始化表单数据
*/
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
initPage()
}
}
)
/**
* 初始化页面数据
*/
const initPage = async () => {
// 如果有数据,则填充数据
if (props.data) {
await nextTick()
refreshData()
}
}
const refreshData = () => {
searchForm.value.group_id = props.data?.id
Object.assign(searchParams, searchForm.value)
getData()
}
const handleAddClick = () => {
showDialog('add', { group_id: searchForm.value.group_id })
}
const searchForm = ref({
label: '',
value: '',
status: '',
group_id: null
})
const {
loading,
data: tableData,
columns,
getData,
pagination,
searchParams,
handleSortChange,
handleSizeChange,
handleCurrentChange
} = useTable({
core: {
apiFn: api.configList,
immediate: false,
apiParams: {
...searchForm.value
},
columnsFactory: () => [
{ type: 'selection' },
{ prop: 'key', label: '配置标识' },
{ prop: 'name', label: '配置标题' },
{ prop: 'input_type', label: '组件类型', width: 100 },
{ prop: 'sort', label: '排序', width: 100, sortable: true },
{ prop: 'remark', label: '备注' },
{ prop: 'operation', label: '操作', useSlot: true, width: 100 }
]
}
})
// 编辑配置
const {
dialogType,
dialogVisible,
dialogData,
showDialog,
deleteRow,
deleteSelectedRows,
handleSelectionChange,
selectedRows
} = useSaiAdmin()
/**
* 关闭弹窗并重置表单
*/
const handleClose = () => {
visible.value = false
emit('success')
}
</script>

View File

@@ -0,0 +1,76 @@
<template>
<sa-search-bar
ref="searchBarRef"
v-model="formData"
label-width="80px"
:showExpand="false"
@reset="handleReset"
@search="handleSearch"
@expand="handleExpand"
>
<el-col v-bind="setSpan(6)">
<el-form-item label="字典标签" prop="label">
<el-input v-model="formData.label" placeholder="请输入字典标签" />
</el-form-item>
</el-col>
<el-col v-bind="setSpan(6)">
<el-form-item label="字典键值" prop="value">
<el-input v-model="formData.value" placeholder="请输入字典键值" />
</el-form-item>
</el-col>
<el-col v-bind="setSpan(6)">
<el-form-item label="状态" prop="status">
<sa-select v-model="formData.status" dict="data_status" />
</el-form-item>
</el-col>
</sa-search-bar>
</template>
<script setup lang="ts">
interface Props {
modelValue: Record<string, any>
}
interface Emits {
(e: 'update:modelValue', value: Record<string, any>): void
(e: 'search', params: Record<string, any>): void
(e: 'reset'): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const isExpanded = ref<boolean>(false)
// 表单数据双向绑定
const searchBarRef = ref()
const formData = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
// 重置
function handleReset() {
searchBarRef.value?.ref.resetFields()
emit('reset')
}
// 搜索
async function handleSearch() {
emit('search', formData.value)
}
// 展开/收起
function handleExpand(expanded: boolean) {
isExpanded.value = expanded
}
// 栅格占据的列数
const setSpan = (span: number) => {
return {
span: span,
xs: 24, // 手机:满宽显示
sm: span >= 12 ? span : 12, // 平板大于等于12保持否则用半宽
md: span >= 8 ? span : 8, // 中等屏幕大于等于8保持否则用三分之一宽
lg: span,
xl: span
}
}
</script>

View File

@@ -0,0 +1,151 @@
<template>
<el-dialog
v-model="visible"
:title="dialogType === 'add' ? '新增配置分组' : '编辑配置分组'"
width="600px"
align-center
@close="handleClose"
>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="配置名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入配置名称" />
</el-form-item>
<el-form-item label="配置标识" prop="code">
<el-input v-model="formData.code" placeholder="请输入配置标识" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import api from '@/api/system/config'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
interface Props {
modelValue: boolean
dialogType: string
data?: Record<string, any>
}
interface Emits {
(e: 'update:modelValue', value: boolean): void
(e: 'success'): void
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
dialogType: 'add',
data: undefined
})
const emit = defineEmits<Emits>()
const formRef = ref<FormInstance>()
/**
* 弹窗显示状态双向绑定
*/
const visible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
/**
* 表单验证规则
*/
const rules = reactive<FormRules>({
name: [{ required: true, message: '请输入配置名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入配置标识', trigger: 'blur' }]
})
/**
* 初始数据
*/
const initialFormData = {
id: null,
name: '',
code: '',
remark: ''
}
/**
* 表单数据
*/
const formData = reactive({ ...initialFormData })
/**
* 监听弹窗打开,初始化表单数据
*/
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
initPage()
}
}
)
/**
* 初始化页面数据
*/
const initPage = async () => {
// 先重置为初始值
Object.assign(formData, initialFormData)
// 如果有数据,则填充数据
if (props.data) {
await nextTick()
initForm()
}
}
/**
* 初始化表单数据
*/
const initForm = () => {
if (props.data) {
for (const key in formData) {
if (props.data[key] != null && props.data[key] != undefined) {
;(formData as any)[key] = props.data[key]
}
}
}
}
/**
* 关闭弹窗并重置表单
*/
const handleClose = () => {
visible.value = false
formRef.value?.resetFields()
}
/**
* 提交表单
*/
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
if (props.dialogType === 'add') {
await api.save(formData)
ElMessage.success('新增成功')
} else {
await api.update(formData)
ElMessage.success('修改成功')
}
emit('success')
handleClose()
} catch (error) {
console.log('表单验证失败:', error)
}
}
</script>