init
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import request from '../utils/request'
|
||||
|
||||
export function get<<.ModelName>>List(params) {
|
||||
return request({
|
||||
url: '/<<.ModuleName>>s',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function get<<.ModelName>>Detail(id) {
|
||||
return request({
|
||||
url: `/<<.ModuleName>>s/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
<<if .HasCreate>>
|
||||
export function create<<.ModelName>>(data) {
|
||||
return request({
|
||||
url: '/<<.ModuleName>>s',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
<<end>>
|
||||
|
||||
<<if .HasEdit>>
|
||||
export function update<<.ModelName>>(id, data) {
|
||||
return request({
|
||||
url: `/<<.ModuleName>>s/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
<<end>>
|
||||
|
||||
<<if .HasDelete>>
|
||||
export function delete<<.ModelName>>(id) {
|
||||
return request({
|
||||
url: `/<<.ModuleName>>s/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
<<end>>
|
||||
@@ -0,0 +1,143 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
|
||||
apperrors "goravel/app/errors"
|
||||
"goravel/app/http/helpers"
|
||||
adminrequests "goravel/app/http/requests/admin"
|
||||
"goravel/app/http/response"
|
||||
"goravel/app/services"
|
||||
)
|
||||
|
||||
type <<.ControllerName>> struct {
|
||||
<<.ServiceName>> services.<<.ServiceName>>
|
||||
}
|
||||
|
||||
func New<<.ControllerName>>() *<<.ControllerName>> {
|
||||
return &<<.ControllerName>>{
|
||||
<<.ServiceName>>: services.New<<.ServiceName>>(),
|
||||
}
|
||||
}
|
||||
|
||||
// Index <<.ModelName>>列表
|
||||
func (c *<<.ControllerName>>) Index(ctx http.Context) http.Response {
|
||||
page := helpers.GetIntQuery(ctx, "page",1)
|
||||
pageSize := helpers.GetIntQuery(ctx, "page_size", 10)
|
||||
|
||||
<<range .SearchableFields>>
|
||||
<<.Name>> := ctx.Request().Query("<<.Name>>", "")
|
||||
<<- end>>
|
||||
|
||||
filters := services.<<.ModelName>>Filters{
|
||||
<<range .SearchableFields>>
|
||||
<<.PascalName>>: <<.Name>>,
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
list, total, err := c.<<.ServiceName>>.GetList(filters, page, pageSize)
|
||||
if err != nil {
|
||||
if businessErr, ok := apperrors.GetBusinessError(err); ok {
|
||||
return response.Error(ctx, http.StatusInternalServerError, businessErr.Code)
|
||||
}
|
||||
return response.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return response.Success(ctx, http.Json{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
// Show <<.ModelName>>详情
|
||||
func (c *<<.ControllerName>>) Show(ctx http.Context) http.Response {
|
||||
id := helpers.GetUintRoute(ctx, "id")
|
||||
item, err := c.<<.ServiceName>>.GetByID(id)
|
||||
if err != nil {
|
||||
if businessErr, ok := apperrors.GetBusinessError(err); ok {
|
||||
return response.Error(ctx, http.StatusNotFound, businessErr.Code)
|
||||
}
|
||||
return response.Error(ctx, http.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return response.Success(ctx, http.Json{
|
||||
"<<.ModuleName>>": item,
|
||||
})
|
||||
}
|
||||
|
||||
// Store 创建<<.ModelName>>
|
||||
func (c *<<.ControllerName>>) Store(ctx http.Context) http.Response {
|
||||
<<if .HasCreate>>
|
||||
var req adminrequests.<<.RequestCreateName>>
|
||||
errors, err := ctx.Request().ValidateRequest(&req)
|
||||
if err != nil {
|
||||
return response.Error(ctx, http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if errors != nil {
|
||||
return response.ValidationError(ctx, http.StatusBadRequest, "validation_failed", errors.All())
|
||||
}
|
||||
|
||||
item, err := c.<<.ServiceName>>.Create(&req)
|
||||
if err != nil {
|
||||
if businessErr, ok := apperrors.GetBusinessError(err); ok {
|
||||
return response.Error(ctx, http.StatusInternalServerError, businessErr.Code)
|
||||
}
|
||||
return response.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return response.Success(ctx, http.Json{
|
||||
"<<.ModuleName>>": item,
|
||||
})
|
||||
<<else>>
|
||||
return response.Error(ctx, http.StatusForbidden, "create_not_allowed")
|
||||
<<end>>
|
||||
}
|
||||
|
||||
// Update 更新<<.ModelName>>
|
||||
func (c *<<.ControllerName>>) Update(ctx http.Context) http.Response {
|
||||
<<if .HasEdit>>
|
||||
id := helpers.GetUintRoute(ctx, "id")
|
||||
|
||||
var req adminrequests.<<.RequestUpdateName>>
|
||||
errors, err := ctx.Request().ValidateRequest(&req)
|
||||
if err != nil {
|
||||
return response.Error(ctx, http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if errors != nil {
|
||||
return response.ValidationError(ctx, http.StatusBadRequest, "validation_failed", errors.All())
|
||||
}
|
||||
|
||||
item, err := c.<<.ServiceName>>.Update(id, &req)
|
||||
if err != nil {
|
||||
if businessErr, ok := apperrors.GetBusinessError(err); ok {
|
||||
return response.Error(ctx, http.StatusInternalServerError, businessErr.Code)
|
||||
}
|
||||
return response.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return response.Success(ctx, http.Json{
|
||||
"<<.ModuleName>>": item,
|
||||
})
|
||||
<<else>>
|
||||
return response.Error(ctx, http.StatusForbidden, "update_not_allowed")
|
||||
<<end>>
|
||||
}
|
||||
|
||||
// Destroy 删除<<.ModelName>>
|
||||
func (c *<<.ControllerName>>) Destroy(ctx http.Context) http.Response {
|
||||
<<if .HasDelete>>
|
||||
id := helpers.GetUintRoute(ctx, "id")
|
||||
if err := c.<<.ServiceName>>.Delete(id); err != nil {
|
||||
if businessErr, ok := apperrors.GetBusinessError(err); ok {
|
||||
return response.Error(ctx, http.StatusInternalServerError, businessErr.Code)
|
||||
}
|
||||
return response.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return response.Success(ctx, "delete_success", http.Json{})
|
||||
<<else>>
|
||||
return response.Error(ctx, http.StatusForbidden, "delete_not_allowed")
|
||||
<<end>>
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="editId ? $t('common.edit') : $t('common.add')"
|
||||
width="600px"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
>
|
||||
<<range .FormFields>>
|
||||
<el-form-item :label="$t('<<$.ModuleName>>.<<.Name>>')" prop="<<.Name>>">
|
||||
<<if eq .FormType "input">>
|
||||
<el-input v-model="form.<<.Name>>" :placeholder="$t('<<$.ModuleName>>.<<.Name>>')" />
|
||||
<<else if eq .FormType "textarea">>
|
||||
<el-input
|
||||
v-model="form.<<.Name>>"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
:placeholder="$t('<<$.ModuleName>>.<<.Name>>')" />
|
||||
<<else if eq .FormType "select">>
|
||||
<el-select v-model="form.<<.Name>>" :placeholder="$t('common.select')">
|
||||
<el-option
|
||||
v-for="item in <<.Name>>Options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<<else if eq .FormType "switch">>
|
||||
<el-switch v-model="form.<<.Name>>" />
|
||||
<<else if eq .FormType "date-picker">>
|
||||
<el-date-picker
|
||||
v-model="form.<<.Name>>"
|
||||
type="date"
|
||||
:placeholder="$t('common.select_date')" style="width: 100%" />
|
||||
<<else if eq .FormType "datetime-picker">>
|
||||
<el-date-picker
|
||||
v-model="form.<<.Name>>"
|
||||
type="datetime"
|
||||
:placeholder="$t('common.select_datetime')" style="width: 100%" />
|
||||
<<else if eq .FormType "image-upload">>
|
||||
<el-upload
|
||||
class="image-uploader"
|
||||
action="/api/admin/attachments/upload"
|
||||
:show-file-list="false"
|
||||
:on-success="handleImageSuccess"
|
||||
>
|
||||
<img v-if="form.<<.Name>>" :src="form.<<.Name>>" class="image-preview" />
|
||||
<el-icon v-else class="image-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
<<else if eq .FormType "file-upload">>
|
||||
<el-upload
|
||||
action="/api/admin/attachments/upload"
|
||||
:show-file-list="true"
|
||||
:on-success="handleFileSuccess"
|
||||
>
|
||||
<el-button type="primary">{{ $t('common.upload') }}</el-button>
|
||||
</el-upload>
|
||||
<<end>>
|
||||
</el-form-item>
|
||||
<<- end>>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSubmit">
|
||||
{{ $t('common.confirm') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import {
|
||||
<<if .HasCreate>>create<<.ModelName>>,<<end>>
|
||||
<<if .HasEdit>>update<<.ModelName>>,<<end>>
|
||||
get<<.ModelName>>Detail
|
||||
} from '../../api/<<.ModuleName>>'
|
||||
import { getOptions } from '../../api/option'
|
||||
import ErrorHandler from '../../utils/errorHandler'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
editId: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const { t } = useI18n()
|
||||
const formRef = ref(null)
|
||||
const submitting = ref(false)
|
||||
|
||||
<<range .FormFields>>
|
||||
<<if eq .FormType "select">>
|
||||
const <<.Name>>Options = ref([])
|
||||
<<end>>
|
||||
<<- end>>
|
||||
|
||||
const visible = ref(props.modelValue)
|
||||
watch(() => props.modelValue, (val) => {
|
||||
visible.value = val
|
||||
})
|
||||
watch(visible, (val) => {
|
||||
emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
const form = ref({
|
||||
<<range .FormFields>>
|
||||
<<.Name>>: null,
|
||||
<<- end>>
|
||||
})
|
||||
|
||||
const rules = {
|
||||
<<range .FormFields>>
|
||||
<<.Name>>: [
|
||||
{ required: <<.Required>>, message: t('<<$.ModuleName>>.<<.Name>>_required'), trigger: 'blur' }
|
||||
],
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
submitting.value = true
|
||||
|
||||
if (props.editId) {
|
||||
<<if .HasEdit>>
|
||||
await update<<.ModelName>>(props.editId, form.value)
|
||||
ElMessage.success(t('common.update_success'))
|
||||
<<else>>
|
||||
ElMessage.error(t('common.operation_failed'))
|
||||
<<end>>
|
||||
} else {
|
||||
<<if .HasCreate>>
|
||||
await create<<.ModelName>>(form.value)
|
||||
ElMessage.success(t('common.create_success'))
|
||||
<<else>>
|
||||
ElMessage.error(t('common.operation_failed'))
|
||||
<<end>>
|
||||
}
|
||||
|
||||
emit('success')
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
ErrorHandler.handle(error)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
const handleImageSuccess = (response) => {
|
||||
form.value.image = response.data.url
|
||||
}
|
||||
|
||||
const handleFileSuccess = (response) => {
|
||||
form.value.file = response.data.url
|
||||
}
|
||||
|
||||
const loadOptions = async () => {
|
||||
<<range .FormFields>>
|
||||
<<if eq .FormType "select">>
|
||||
try {
|
||||
<<if eq .Dictionary "">>
|
||||
// 未配置字典,请自行实现选项加载逻辑
|
||||
// const res = await getOptions('<<.Name>>')
|
||||
// <<.Name>>Options.value = res.data
|
||||
<<else>>
|
||||
const res = await getOptions('dictionary', { dictionary_type: '<<.Dictionary>>' })
|
||||
if (res.data) {
|
||||
<<.Name>>Options.value = res.data
|
||||
}
|
||||
<<end>>
|
||||
} catch (error) {
|
||||
console.error('Failed to load <<.Name>> options:', error)
|
||||
}
|
||||
<<end>>
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
if (!props.editId) return
|
||||
|
||||
try {
|
||||
const res = await get<<.ModelName>>Detail(props.editId)
|
||||
if (res.data && res.data.<<.ModuleName>>) {
|
||||
const data = res.data.<<.ModuleName>>
|
||||
form.value = {
|
||||
<<range .FormFields>>
|
||||
<<.Name>>: data.<<.Name>>,
|
||||
<<- end>>
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
ErrorHandler.handle(error)
|
||||
}
|
||||
}
|
||||
|
||||
watch(visible, (val) => {
|
||||
if (val) {
|
||||
loadOptions()
|
||||
if (props.editId) {
|
||||
loadData()
|
||||
} else {
|
||||
formRef.value?.resetFields()
|
||||
form.value = {
|
||||
<<range .FormFields>>
|
||||
<<.Name>>: null,
|
||||
<<- end>>
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.image-uploader {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-uploader:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.image-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div class="<<.ModuleName>>-list">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ $t('menu.<<$.ModuleName>>') }}</span>
|
||||
<<if .HasCreate>>
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
<el-icon><Plus /></el-icon>
|
||||
{{ $t('common.add') }}
|
||||
</el-button>
|
||||
<<end>>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<SearchForm
|
||||
:model="searchForm"
|
||||
:fields="searchFields"
|
||||
:initial-values="initialSearchForm"
|
||||
i18n-prefix="<<.ModuleName>>"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
/>
|
||||
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="tableData"
|
||||
:loading="loading"
|
||||
:columns="tableColumns"
|
||||
:height="600"
|
||||
@sort-change="handleSortChange"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<<if .HasEdit>>
|
||||
<el-button type="primary" size="small" @click="handleEdit(row)">
|
||||
{{ $t('common.edit') }}
|
||||
</el-button>
|
||||
<<end>>
|
||||
<<if .HasDelete>>
|
||||
<el-button type="danger" size="small" @click="handleDelete(row)">
|
||||
{{ $t('common.delete') }}
|
||||
</el-button>
|
||||
<<end>>
|
||||
</template>
|
||||
</VxeTable>
|
||||
|
||||
<Pagination
|
||||
v-model="pagination"
|
||||
:auto-load="true"
|
||||
:on-page-change="loadData"
|
||||
/>
|
||||
|
||||
<<.ModelName>>Form
|
||||
ref="formRef"
|
||||
v-model="dialogVisible"
|
||||
:edit-id="editId"
|
||||
@success="handleFormSuccess"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import SearchForm from '../../components/SearchForm.vue'
|
||||
import Pagination from '../../components/Pagination.vue'
|
||||
import VxeTable from '../../components/VxeTable.vue'
|
||||
import <<.ModelName>>Form from './<<.ModelName>>Form.vue'
|
||||
import { useListPage } from '../../composables/useListPage'
|
||||
import { useCrud } from '../../composables/useCrud'
|
||||
import {
|
||||
get<<.ModelName>>List,
|
||||
delete<<.ModelName>>
|
||||
} from '../../api/<<.ModuleName>>'
|
||||
import logger from '../../utils/logger'
|
||||
import ErrorHandler from '../../utils/errorHandler'
|
||||
|
||||
const { t } = useI18n()
|
||||
const tableRef = ref(null)
|
||||
const formRef = ref(null)
|
||||
|
||||
const {
|
||||
dialogVisible,
|
||||
editId,
|
||||
handleAdd,
|
||||
handleClose,
|
||||
handleDelete: handleDeleteCrud
|
||||
} = useCrud({
|
||||
deleteApi: delete<<.ModelName>>
|
||||
})
|
||||
|
||||
const initialSearchForm = {
|
||||
<<range .SearchableFields>>
|
||||
<<.Name>>: '',
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
const {
|
||||
pagination,
|
||||
tableData,
|
||||
loading,
|
||||
searchForm,
|
||||
loadData,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
handleSortChange
|
||||
} = useListPage({
|
||||
fetchApi: get<<.ModelName>>List,
|
||||
initialSearchForm,
|
||||
fieldMapping: {},
|
||||
defaultSort: 'id:desc',
|
||||
tableRef: computed(() => tableRef.value?.tableRef)
|
||||
})
|
||||
|
||||
const searchFields = computed(() => [
|
||||
<<range .SearchableFields>>
|
||||
{
|
||||
prop: '<<.Name>>',
|
||||
label: t('<<$.ModuleName>>.<<.Name>>'),
|
||||
type: '<<.SearchUIType>>',
|
||||
<<if .ApiUrl>> apiUrl: '<<.ApiUrl>>',<<end>>
|
||||
<<if eq .SearchUIType "select">>
|
||||
<<if eq .Dictionary "">>
|
||||
// 如果没有配置字典,可能是模块选项(如role, department等),apiUrl已由.ApiUrl提供
|
||||
<<else>>
|
||||
apiUrl: '/options?type=dictionary&dictionary_type=<<.Dictionary>>',
|
||||
<<end>>
|
||||
<<end>>
|
||||
width: '200px',
|
||||
advanced: false
|
||||
},
|
||||
<<- end>>
|
||||
])
|
||||
|
||||
const tableColumns = computed(() => [
|
||||
{
|
||||
field: 'id',
|
||||
title: t('table.id'),
|
||||
width: 80,
|
||||
sortable: true
|
||||
},
|
||||
<<range .ListFields>>
|
||||
{
|
||||
field: '<<.Name>>',
|
||||
<<if .Relation>>
|
||||
title: t('<<$.Relation.Table>>.<<$.Relation.DisplayField>>'),
|
||||
<<else>>
|
||||
title: t('<<$.ModuleName>>.<<.Name>>'),
|
||||
<<end>>
|
||||
sortable: <<.Sortable>>
|
||||
},
|
||||
<<if .Relation>>
|
||||
{
|
||||
field: '<<.Relation.Table>>_<<.Relation.DisplayField>>',
|
||||
title: t('<<$.Relation.Table>>.<<$.Relation.DisplayField>>'),
|
||||
sortable: false
|
||||
},
|
||||
<<end>>
|
||||
<<- end>>
|
||||
{
|
||||
field: 'created_at',
|
||||
title: t('table.created_at'),
|
||||
width: 180,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'operation',
|
||||
title: t('table.operation'),
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
slot: 'operation'
|
||||
}
|
||||
])
|
||||
|
||||
const handleEdit = (row) => {
|
||||
editId.value = row.id
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleDelete = async (row) => {
|
||||
await handleDeleteCrud(row, loadData)
|
||||
}
|
||||
|
||||
const handleFormSuccess = () => {
|
||||
handleClose()
|
||||
loadData()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.<<.ModuleName>>-list {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/database/schema"
|
||||
"github.com/goravel/framework/facades"
|
||||
)
|
||||
|
||||
type M<<.Timestamp>>Create<<.ModelName>>Table struct {
|
||||
}
|
||||
|
||||
func (m *M<<.Timestamp>>Create<<.ModelName>>Table) Signature() string {
|
||||
return "<<.Timestamp>>_create_<<.TableName>>_table"
|
||||
}
|
||||
|
||||
func (m *M<<.Timestamp>>Create<<.ModelName>>Table) Up() error {
|
||||
return facades.Schema().Create("<<.TableName>>", func(table schema.Blueprint) {
|
||||
table.ID()
|
||||
<<range .Fields>>
|
||||
table.<<.MigrationMethod>>("<<.Name>>")<<if .Comment>>.Comment("<<.Comment>>")<<end>>
|
||||
<<- end>>
|
||||
table.Timestamps()
|
||||
table.SoftDeletes()
|
||||
})
|
||||
}
|
||||
func (m *M<<.Timestamp>>Create<<.ModelName>>Table) Down() error {
|
||||
return facades.Schema().DropIfExists("<<.TableName>>")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/database/orm"
|
||||
)
|
||||
|
||||
type <<.ModelName>> struct {
|
||||
orm.Model
|
||||
orm.SoftDeletes
|
||||
<<range .Fields>>
|
||||
<<.FieldName>> <<.GoType>> `gorm:"<<.Name>>" json:"<<.JsonName>>"<<if .Comment>> comment:"<<.Comment>>"<<end>><<if .Relation>> json:"<<.Relation.Table>>_<<.Relation.DisplayField>>" gorm:"<<.Relation.Table>>;foreignKey:<<.Relation.ForeignKey>>"<<end>>`
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
func (<<.ModelName>>) TableName() string {
|
||||
return "<<.TableName>>"
|
||||
}
|
||||
|
||||
func (r *<<.ModelName>>) Serialize() map[string]any {
|
||||
return map[string]any{
|
||||
"id": r.ID,
|
||||
"created_at": r.CreatedAt,
|
||||
"updated_at": r.UpdatedAt,
|
||||
<<range .Fields>>
|
||||
"<<.JsonName>>": r.<<.FieldName>>,
|
||||
<<end>>
|
||||
}
|
||||
}
|
||||
|
||||
func (r *<<.ModelName>>) Deserialize(data map[string]any) {
|
||||
<<range .Fields>>
|
||||
if val, ok := data["<<.JsonName>>"]; ok {
|
||||
r.<<.FieldName>> = val.(<<.GoType>>)
|
||||
}
|
||||
<<end>>
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"goravel/app/http/trans"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
)
|
||||
|
||||
type <<.RequestCreateName>> struct {
|
||||
<<range .FormFields>>
|
||||
<<.FieldName>> <<.GoType>> `form:"<<.JsonName>>" json:"<<.JsonName>>"`
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
func (r *<<.RequestCreateName>>) Authorize(ctx http.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *<<.RequestCreateName>>) Rules(ctx http.Context) map[string]string {
|
||||
rules := map[string]string{
|
||||
<<range .FormFields>>
|
||||
"<<.JsonName>>": "<<if .Required>>required<<end>><<if and .Required .Validators>>|<<end>><<range $i, $v := .Validators>><<if $i>>|<<end>><<$v>><<end>>",
|
||||
<<- end>>
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
func (r *<<.RequestCreateName>>) Messages(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
<<range .FormFields>>
|
||||
"<<.JsonName>>.required": trans.Get(ctx, "validation_<<.Name>>_required"),
|
||||
<<- end>>
|
||||
}
|
||||
}
|
||||
|
||||
func (r *<<.RequestCreateName>>) Attributes(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
<<range .FormFields>>
|
||||
"<<.JsonName>>": trans.Get(ctx, "validation_<<.Name>>"),
|
||||
<<- end>>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"goravel/app/http/trans"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
)
|
||||
|
||||
type <<.RequestUpdateName>> struct {
|
||||
<<range .FormFields>>
|
||||
<<.FieldName>> *<<.GoType>> `form:"<<.JsonName>>" json:"<<.JsonName>>"`
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
func (r *<<.RequestUpdateName>>) Authorize(ctx http.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *<<.RequestUpdateName>>) Rules(ctx http.Context) map[string]string {
|
||||
rules := map[string]string{
|
||||
<<range .FormFields>>
|
||||
"<<.JsonName>>": "<<if .Required>>required<<end>><<if and .Required .Validators>>|<<end>><<range $i, $v := .Validators>><<if $i>>|<<end>><<$v>><<end>>",
|
||||
<<- end>>
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
func (r *<<.RequestUpdateName>>) Messages(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
<<range .FormFields>>
|
||||
"<<.JsonName>>.required": trans.Get(ctx, "validation_<<.Name>>_required"),
|
||||
<<- end>>
|
||||
}
|
||||
}
|
||||
|
||||
func (r *<<.RequestUpdateName>>) Attributes(ctx http.Context) map[string]string {
|
||||
return map[string]string{
|
||||
<<range .FormFields>>
|
||||
"<<.JsonName>>": trans.Get(ctx, "validation_<<.Name>>"),
|
||||
<<- end>>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/database/orm"
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
apperrors "goravel/app/errors"
|
||||
"goravel/app/http/requests/admin"
|
||||
"goravel/app/models"
|
||||
)
|
||||
|
||||
type <<.ServiceName>> interface {
|
||||
GetByID(id uint) (*models.<<.ModelName>>, error)
|
||||
GetList(filters <<.ModelName>>Filters, page, pageSize int) ([]models.<<.ModelName>>, int64, error)
|
||||
<<if .HasCreate>>
|
||||
Create(req *admin.<<.RequestCreateName>>) (*models.<<.ModelName>>, error)
|
||||
<<end>>
|
||||
<<if .HasEdit>>
|
||||
Update(id uint, req *admin.<<.RequestUpdateName>>) (*models.<<.ModelName>>, error)
|
||||
<<end>>
|
||||
<<if .HasDelete>>
|
||||
Delete(id uint) error
|
||||
<<end>>
|
||||
}
|
||||
|
||||
type <<.ModelName>>Filters struct {
|
||||
<<range .SearchableFields>>
|
||||
<<.PascalName>> string
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
type <<.ServiceName>>Impl struct{}
|
||||
|
||||
func New<<.ServiceName>>() <<.ServiceName>> {
|
||||
return &<<.ServiceName>>Impl{}
|
||||
}
|
||||
|
||||
func Build<<.ModelName>>Query(filters <<.ModelName>>Filters) orm.Query {
|
||||
query := facades.Orm().Query().Model(&models.<<.ModelName>>{})
|
||||
<<range .SearchableFields>>
|
||||
if filters.<<.PascalName>> != "" {
|
||||
<<if eq .SearchType "like">>
|
||||
query = query.Where("<<.Name>> LIKE ?", "%"+filters.<<.PascalName>>+"%")
|
||||
<<else if eq .SearchType "=">>
|
||||
query = query.Where("<<.Name>> = ?", filters.<<.PascalName>>)
|
||||
<<else if eq .SearchType ">" >>
|
||||
query = query.Where("<<.Name>> > ?", filters.<<.PascalName>>)
|
||||
<<else if eq .SearchType ">=" >>
|
||||
query = query.Where("<<.Name>> >= ?", filters.<<.PascalName>>)
|
||||
<<else if eq .SearchType "<" >>
|
||||
query = query.Where("<<.Name>> < ?", filters.<<.PascalName>>)
|
||||
<<else if eq .SearchType "<=" >>
|
||||
query = query.Where("<<.Name>> <= ?", filters.<<.PascalName>>)
|
||||
<<else if eq .SearchType "!=" >>
|
||||
query = query.Where("<<.Name>> != ?", filters.<<.PascalName>>)
|
||||
<<else if eq .SearchType "in">>
|
||||
query = query.Where("<<.Name>> IN ?", filters.<<.PascalName>>)
|
||||
<<else>>
|
||||
query = query.Where("<<.Name>> LIKE ?", "%"+filters.<<.PascalName>>+"%")
|
||||
<<end>>
|
||||
}
|
||||
<<- end>>
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
func (s *<<.ServiceName>>Impl) GetByID(id uint) (*models.<<.ModelName>>, error) {
|
||||
var item models.<<.ModelName>>
|
||||
if err := facades.Orm().Query().Where("id", id).FirstOrFail(&item); err != nil {
|
||||
return nil, apperrors.NewBusinessError("<<.ModuleName>>_not_found", "<<.ModelName>> not found").WithError(err)
|
||||
}
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
func (s *<<.ServiceName>>Impl) GetList(filters <<.ModelName>>Filters, page, pageSize int) ([]models.<<.ModelName>>, int64, error) {
|
||||
query := Build<<.ModelName>>Query(filters)
|
||||
|
||||
var list []models.<<.ModelName>>
|
||||
var total int64
|
||||
if err := query.Order("id desc").Paginate(page, pageSize, &list, &total); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
<<if .HasCreate>>
|
||||
func (s *<<.ServiceName>>Impl) Create(req *admin.<<.RequestCreateName>>) (*models.<<.ModelName>>, error) {
|
||||
item := &models.<<.ModelName>>{
|
||||
<<range .FormFields>>
|
||||
<<.FieldName>>: req.<<.FieldName>>,
|
||||
<<- end>>
|
||||
}
|
||||
|
||||
if err := facades.Orm().Query().Create(item); err != nil {
|
||||
return nil, apperrors.ErrCreateFailed.WithError(err)
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
<<end>>
|
||||
|
||||
<<if .HasEdit>>
|
||||
func (s *<<.ServiceName>>Impl) Update(id uint, req *admin.<<.RequestUpdateName>>) (*models.<<.ModelName>>, error) {
|
||||
item, err := s.GetByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
<<range .FormFields>>
|
||||
if req.<<.FieldName>> != nil {
|
||||
item.<<.FieldName>> = *req.<<.FieldName>>
|
||||
}
|
||||
<<- end>>
|
||||
|
||||
if err := facades.Orm().Query().Save(item); err != nil {
|
||||
return nil, apperrors.ErrUpdateFailed.WithError(err)
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
<<end>>
|
||||
|
||||
<<if .HasDelete>>
|
||||
func (s *<<.ServiceName>>Impl) Delete(id uint) error {
|
||||
if _, err := facades.Orm().Query().Where("id", id).Delete(&models.<<.ModelName>>{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
<<end>>
|
||||
Reference in New Issue
Block a user