mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-12 02:45:49 +02:00
feat: add category option for site
This commit is contained in:
parent
7ad5cac3b8
commit
207f80f858
16 changed files with 1452 additions and 508 deletions
|
@ -69,7 +69,7 @@ func GetSite(c *gin.Context) {
|
|||
|
||||
c.JSON(http.StatusOK, Site{
|
||||
ModifiedAt: file.ModTime(),
|
||||
Advanced: site.Advanced,
|
||||
Site: site,
|
||||
Enabled: enabled,
|
||||
Name: name,
|
||||
Config: string(origContent),
|
||||
|
@ -102,7 +102,7 @@ func GetSite(c *gin.Context) {
|
|||
|
||||
c.JSON(http.StatusOK, Site{
|
||||
ModifiedAt: file.ModTime(),
|
||||
Advanced: site.Advanced,
|
||||
Site: site,
|
||||
Enabled: enabled,
|
||||
Name: name,
|
||||
Config: nginxConfig.FmtCode(),
|
||||
|
@ -127,6 +127,7 @@ func SaveSite(c *gin.Context) {
|
|||
var json struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
SiteCategoryID uint64 `json:"site_category_id"`
|
||||
Overwrite bool `json:"overwrite"`
|
||||
}
|
||||
|
||||
|
@ -149,11 +150,18 @@ func SaveSite(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
enabledConfigFilePath := nginx.GetConfPath("sites-enabled", name)
|
||||
s := query.Site
|
||||
|
||||
_, err = s.Where(s.Path.Eq(path)).Update(s.SiteCategoryID, json.SiteCategoryID)
|
||||
if err != nil {
|
||||
api.ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// rename the config file if needed
|
||||
if name != json.Name {
|
||||
newPath := nginx.GetConfPath("sites-available", json.Name)
|
||||
s := query.Site
|
||||
_, err = s.Where(s.Path.Eq(path)).Update(s.Path, newPath)
|
||||
_, _ = s.Where(s.Path.Eq(path)).Update(s.Path, newPath)
|
||||
|
||||
// check if dst file exists, do not rename
|
||||
if helper.FileExists(newPath) {
|
||||
|
|
|
@ -3,15 +3,16 @@ package sites
|
|||
import (
|
||||
"github.com/0xJacky/Nginx-UI/internal/cert"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Site struct {
|
||||
ModifiedAt time.Time `json:"modified_at"`
|
||||
Advanced bool `json:"advanced"`
|
||||
Enabled bool `json:"enabled"`
|
||||
*model.Site
|
||||
Name string `json:"name"`
|
||||
ModifiedAt time.Time `json:"modified_at"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Config string `json:"config"`
|
||||
AutoCert bool `json:"auto_cert"`
|
||||
ChatGPTMessages []openai.ChatCompletionMessage `json:"chatgpt_messages,omitempty"`
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
"reconnecting-websocket": "^4.4.0",
|
||||
"sortablejs": "^1.15.3",
|
||||
"universal-cookie": "^7.2.1",
|
||||
"unocss": "^0.63.6",
|
||||
"vite-plugin-build-id": "0.4.2",
|
||||
"vue": "^3.5.12",
|
||||
"vue-dompurify-html": "^5.1.0",
|
||||
|
@ -51,6 +52,12 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^3.8.0",
|
||||
"@iconify-json/fa": "1.1.5",
|
||||
"@iconify-json/tabler": "1.1.95",
|
||||
"@iconify/tools": "3.0.5",
|
||||
"@iconify/types": "^2.0.0",
|
||||
"@iconify/utils": "^2.1.33",
|
||||
"@iconify/vue": "4.1.1",
|
||||
"@simplewebauthn/types": "^11.0.0",
|
||||
"@types/lodash": "^4.17.12",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
|
|
1485
app/pnpm-lock.yaml
generated
1485
app/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +0,0 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import type { CertificateInfo } from '@/api/cert'
|
||||
import type { NgxConfig } from '@/api/ngx'
|
||||
import type { ChatComplicationMessage } from '@/api/openai'
|
||||
import type { SiteCategory } from '@/api/site_category'
|
||||
import type { PrivateKeyType } from '@/constants'
|
||||
import Curd from '@/api/curd'
|
||||
import http from '@/lib/http'
|
||||
|
@ -16,6 +17,8 @@ export interface Site {
|
|||
chatgpt_messages: ChatComplicationMessage[]
|
||||
tokenized?: NgxConfig
|
||||
cert_info?: Record<number, CertificateInfo[]>
|
||||
site_category_id: number
|
||||
site_category?: SiteCategory
|
||||
}
|
||||
|
||||
export interface AutoCertRequest {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script setup lang="ts">
|
||||
import type Curd from '@/api/curd'
|
||||
import type { Column } from '@/components/StdDesign/types'
|
||||
import type { Ref } from 'vue'
|
||||
import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
|
||||
import { watchOnce } from '@vueuse/core'
|
||||
import _ from 'lodash'
|
||||
|
||||
const props = defineProps<{
|
||||
placeholder?: string
|
||||
label?: string
|
||||
selectedKey: number | number[] | undefined | null
|
||||
selectionType: 'radio' | 'checkbox'
|
||||
recordValueIndex: string // to index the value of the record
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
|
@ -24,9 +24,19 @@ const props = defineProps<{
|
|||
disabled?: boolean
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
valueApi?: Curd<any>
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
getCheckboxProps?: (record: any) => any
|
||||
hideInputContainer?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:selectedKey'])
|
||||
const selectedKey = defineModel<number | number[] | undefined | null | string | string[]>('selectedKey')
|
||||
|
||||
onMounted(() => {
|
||||
if (!selectedKey.value)
|
||||
watchOnce(selectedKey, _init)
|
||||
else
|
||||
_init()
|
||||
})
|
||||
|
||||
const getParams = computed(() => {
|
||||
return props.getParams
|
||||
|
@ -34,19 +44,23 @@ const getParams = computed(() => {
|
|||
|
||||
const visible = ref(false)
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
const M_values = ref([]) as any
|
||||
const M_values = ref([]) as Ref<any[]>
|
||||
|
||||
const init = _.debounce(_init, 500, {
|
||||
leading: true,
|
||||
trailing: false,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
const ComputedMValue = computed(() => {
|
||||
return M_values.value.filter(v => v && Object.keys(v).length > 0)
|
||||
})
|
||||
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
const records = ref([]) as Ref<any[]>
|
||||
const records = defineModel<any[]>('selectedRecords', {
|
||||
default: () => [],
|
||||
})
|
||||
|
||||
watch(() => props.value, () => {
|
||||
if (props.selectionType === 'radio')
|
||||
M_values.value = [props.value]
|
||||
else if (typeof selectedKey.value === 'object')
|
||||
M_values.value = props.value || []
|
||||
})
|
||||
|
||||
async function _init() {
|
||||
// valueApi is used to fetch items that are using itemKey as index value
|
||||
|
@ -55,22 +69,22 @@ async function _init() {
|
|||
M_values.value = []
|
||||
|
||||
if (props.selectionType === 'radio') {
|
||||
// M_values.value = [props.value] // not init value, we need to fetch them from api
|
||||
if (!props.value && props.selectedKey) {
|
||||
api.get(props.selectedKey, props.getParams).then(r => {
|
||||
// M_values.value = [props.value]
|
||||
// not init value, we need to fetch them from api
|
||||
if (!props.value && selectedKey.value && selectedKey.value !== '0') {
|
||||
api.get(selectedKey.value, props.getParams).then(r => {
|
||||
M_values.value = [r]
|
||||
records.value = [r]
|
||||
})
|
||||
}
|
||||
}
|
||||
else if (typeof props.selectedKey === 'object') {
|
||||
M_values.value = props.value || []
|
||||
|
||||
else if (typeof selectedKey.value === 'object') {
|
||||
// M_values.value = props.value || []
|
||||
// not init value, we need to fetch them from api
|
||||
if (!props.value && (props.selectedKey?.length || 0) > 0) {
|
||||
if (!props.value && (selectedKey.value?.length || 0) > 0) {
|
||||
api.get_list({
|
||||
...props.getParams,
|
||||
id: props.selectedKey,
|
||||
id: selectedKey.value,
|
||||
}).then(r => {
|
||||
M_values.value = r.data
|
||||
records.value = r.data
|
||||
|
@ -85,11 +99,22 @@ function show() {
|
|||
}
|
||||
|
||||
const selectedKeyBuffer = ref()
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
const selectedBuffer: Ref<any[]> = ref([])
|
||||
|
||||
if (props.selectionType === 'radio')
|
||||
selectedKeyBuffer.value = [props.selectedKey]
|
||||
else
|
||||
selectedKeyBuffer.value = props.selectedKey
|
||||
watch(selectedKey, () => {
|
||||
selectedKeyBuffer.value = _.clone(selectedKey.value)
|
||||
})
|
||||
|
||||
watch(records, v => {
|
||||
selectedBuffer.value = [...v]
|
||||
M_values.value = [...v]
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
selectedKeyBuffer.value = _.clone(selectedKey.value)
|
||||
selectedBuffer.value = _.clone(records.value)
|
||||
})
|
||||
|
||||
const computedSelectedKeys = computed({
|
||||
get() {
|
||||
|
@ -103,41 +128,36 @@ const computedSelectedKeys = computed({
|
|||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.selectedKey === undefined || props.selectedKey === null) {
|
||||
if (props.selectionType === 'radio')
|
||||
emit('update:selectedKey', '')
|
||||
else
|
||||
emit('update:selectedKey', [])
|
||||
}
|
||||
})
|
||||
|
||||
async function ok() {
|
||||
visible.value = false
|
||||
emit('update:selectedKey', selectedKeyBuffer.value)
|
||||
|
||||
selectedKey.value = selectedKeyBuffer.value
|
||||
records.value = selectedBuffer.value
|
||||
await nextTick()
|
||||
M_values.value = _.clone(records.value)
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
init()
|
||||
})
|
||||
|
||||
// function clear() {
|
||||
// M_values.value = []
|
||||
// emit('update:selectedKey', '')
|
||||
// }
|
||||
|
||||
defineExpose({ show })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="std-selector-container">
|
||||
<div>
|
||||
<div
|
||||
v-if="!hideInputContainer"
|
||||
class="std-selector-container"
|
||||
>
|
||||
<div
|
||||
class="std-selector"
|
||||
@click="show"
|
||||
>
|
||||
<div class="chips-container">
|
||||
<div v-if="props.recordValueIndex">
|
||||
<ATag
|
||||
v-for="(chipText, index) in M_values"
|
||||
v-for="(chipText, index) in ComputedMValue"
|
||||
:key="index"
|
||||
class="mr-1"
|
||||
color="orange"
|
||||
|
@ -147,6 +167,15 @@ watchEffect(() => {
|
|||
{{ chipText?.[recordValueIndex] }}
|
||||
</ATag>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="text-gray-400"
|
||||
>
|
||||
{{ placeholder }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AModal
|
||||
:mask="false"
|
||||
:open="visible"
|
||||
|
@ -161,26 +190,26 @@ watchEffect(() => {
|
|||
{{ description }}
|
||||
<StdTable
|
||||
v-model:selected-row-keys="computedSelectedKeys"
|
||||
v-model:selected-rows="records"
|
||||
:api="api"
|
||||
:columns="columns"
|
||||
:disable-search="disableSearch"
|
||||
pithy
|
||||
v-model:selected-rows="selectedBuffer"
|
||||
:api
|
||||
:columns
|
||||
:disable-search
|
||||
:row-key="itemKey"
|
||||
:get-params="getParams"
|
||||
:selection-type="selectionType"
|
||||
:get-params
|
||||
:selection-type
|
||||
:get-checkbox-props
|
||||
pithy
|
||||
disable-query-params
|
||||
/>
|
||||
</AModal>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.std-selector-container {
|
||||
min-height: 39.9px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
align-items: self-start;
|
||||
|
||||
.std-selector {
|
||||
overflow-y: auto;
|
||||
|
@ -195,7 +224,7 @@ watchEffect(() => {
|
|||
line-height: 1.5;
|
||||
background-image: none;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s;
|
||||
//margin: 0 10px 0 0;
|
||||
cursor: pointer;
|
||||
|
@ -203,9 +232,10 @@ watchEffect(() => {
|
|||
}
|
||||
}
|
||||
|
||||
.chips-container {
|
||||
span {
|
||||
margin: 2px;
|
||||
.dark {
|
||||
.std-selector {
|
||||
border: 1px solid #424242;
|
||||
background-color: #141414;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,7 +7,7 @@ import VueDOMPurifyHTML from 'vue-dompurify-html'
|
|||
import App from './App.vue'
|
||||
import gettext from './gettext'
|
||||
import router from './routes'
|
||||
import './style.css'
|
||||
import 'virtual:uno.css'
|
||||
|
||||
const pinia = createPinia()
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -40,7 +40,7 @@ const saving = ref(false)
|
|||
const filename = ref('')
|
||||
const parse_error_status = ref(false)
|
||||
const parse_error_message = ref('')
|
||||
const data = ref({})
|
||||
const data = ref({}) as Ref<Site>
|
||||
|
||||
init()
|
||||
|
||||
|
@ -134,6 +134,7 @@ async function save() {
|
|||
name: filename.value || name.value,
|
||||
content: configText.value,
|
||||
overwrite: true,
|
||||
site_category_id: data.value.site_category_id,
|
||||
}).then(r => {
|
||||
handle_response(r)
|
||||
router.push({
|
||||
|
|
|
@ -4,10 +4,13 @@ import type { ChatComplicationMessage } from '@/api/openai'
|
|||
import type { CheckedType } from '@/types'
|
||||
import type { Ref } from 'vue'
|
||||
import domain from '@/api/domain'
|
||||
import site_category from '@/api/site_category'
|
||||
import ChatGPT from '@/components/ChatGPT/ChatGPT.vue'
|
||||
import StdSelector from '@/components/StdDesign/StdDataEntry/components/StdSelector.vue'
|
||||
import { formatDateTime } from '@/lib/helper'
|
||||
import { useSettingsStore } from '@/pinia'
|
||||
import Deploy from '@/views/site/components/Deploy.vue'
|
||||
import siteCategoryColumns from '@/views/site/site_category/columns'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
|
||||
const settings = useSettingsStore()
|
||||
|
@ -73,6 +76,7 @@ function on_change_enabled(checked: CheckedType) {
|
|||
key="1"
|
||||
:header="$gettext('Basic')"
|
||||
>
|
||||
<AForm layout="vertical">
|
||||
<AFormItem :label="$gettext('Enabled')">
|
||||
<ASwitch
|
||||
:checked="enabled"
|
||||
|
@ -82,9 +86,19 @@ function on_change_enabled(checked: CheckedType) {
|
|||
<AFormItem :label="$gettext('Name')">
|
||||
<AInput v-model:value="filename" />
|
||||
</AFormItem>
|
||||
<AFormItem :label="$gettext('Category')">
|
||||
<StdSelector
|
||||
v-model:selected-key="data.site_category_id"
|
||||
:api="site_category"
|
||||
:columns="siteCategoryColumns"
|
||||
record-value-index="name"
|
||||
selection-type="radio"
|
||||
/>
|
||||
</AFormItem>
|
||||
<AFormItem :label="$gettext('Updated at')">
|
||||
{{ formatDateTime(data.modified_at) }}
|
||||
</AFormItem>
|
||||
</AForm>
|
||||
</ACollapsePanel>
|
||||
<ACollapsePanel
|
||||
v-if="!settings.is_remote"
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
'./index.html',
|
||||
'./src/**/*.{vue,js,ts,jsx,tsx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
}
|
66
app/uno.config.ts
Normal file
66
app/uno.config.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
// uno.config.ts
|
||||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetTypography,
|
||||
presetUno,
|
||||
presetWebFonts,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from 'unocss'
|
||||
|
||||
export default defineConfig({
|
||||
shortcuts: [],
|
||||
rules: [],
|
||||
variants: [
|
||||
// 使用工具函数
|
||||
matcher => {
|
||||
if (!matcher.endsWith('!'))
|
||||
return matcher
|
||||
return {
|
||||
matcher: matcher.slice(0, -1),
|
||||
selector: s => `${s}!important`,
|
||||
}
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
colors: {
|
||||
// ...
|
||||
},
|
||||
},
|
||||
presets: [
|
||||
presetUno(),
|
||||
presetAttributify(),
|
||||
presetIcons({
|
||||
collections: {
|
||||
tabler: () => import('@iconify-json/tabler/icons.json').then(i => i.default),
|
||||
},
|
||||
extraProperties: {
|
||||
'display': 'inline-block',
|
||||
'height': '1.2em',
|
||||
'width': '1.2em',
|
||||
'vertical-align': 'text-bottom',
|
||||
},
|
||||
}),
|
||||
presetTypography(),
|
||||
presetWebFonts(),
|
||||
],
|
||||
transformers: [
|
||||
transformerDirectives(),
|
||||
transformerVariantGroup(),
|
||||
],
|
||||
content: {
|
||||
pipeline: {
|
||||
include: [
|
||||
// default
|
||||
/\.(vue|[jt]sx|ts)($|\?)/,
|
||||
|
||||
// 参考:https://unocss.dev/guide/extracting#extracting-from-build-tools-pipeline
|
||||
],
|
||||
|
||||
// exclude files
|
||||
// exclude: []
|
||||
},
|
||||
},
|
||||
})
|
|
@ -1,10 +1,10 @@
|
|||
import { fileURLToPath, URL } from 'node:url'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import UnoCSS from 'unocss/vite'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
|
||||
import DefineOptions from 'unplugin-vue-define-options/vite'
|
||||
import { defineConfig, loadEnv } from 'vite'
|
||||
import vitePluginBuildId from 'vite-plugin-build-id'
|
||||
|
@ -34,9 +34,9 @@ export default defineConfig(({ mode }) => {
|
|||
plugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
|
||||
vitePluginBuildId(),
|
||||
svgLoader(),
|
||||
UnoCSS(),
|
||||
Components({
|
||||
resolvers: [AntDesignVueResolver({ importStyle: false })],
|
||||
directoryAsNamespace: true,
|
||||
|
|
|
@ -4,4 +4,6 @@ type Site struct {
|
|||
Model
|
||||
Path string `json:"path"`
|
||||
Advanced bool `json:"advanced"`
|
||||
SiteCategoryID uint64 `json:"site_category_id"`
|
||||
SiteCategory *SiteCategory `json:"site_category,omitempty"`
|
||||
}
|
||||
|
|
|
@ -34,6 +34,12 @@ func newSite(db *gorm.DB, opts ...gen.DOOption) site {
|
|||
_site.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
_site.Path = field.NewString(tableName, "path")
|
||||
_site.Advanced = field.NewBool(tableName, "advanced")
|
||||
_site.SiteCategoryID = field.NewUint64(tableName, "site_category_id")
|
||||
_site.SiteCategory = siteBelongsToSiteCategory{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
RelationField: field.NewRelation("SiteCategory", "model.SiteCategory"),
|
||||
}
|
||||
|
||||
_site.fillFieldMap()
|
||||
|
||||
|
@ -50,6 +56,8 @@ type site struct {
|
|||
DeletedAt field.Field
|
||||
Path field.String
|
||||
Advanced field.Bool
|
||||
SiteCategoryID field.Uint64
|
||||
SiteCategory siteBelongsToSiteCategory
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
@ -72,6 +80,7 @@ func (s *site) updateTableName(table string) *site {
|
|||
s.DeletedAt = field.NewField(table, "deleted_at")
|
||||
s.Path = field.NewString(table, "path")
|
||||
s.Advanced = field.NewBool(table, "advanced")
|
||||
s.SiteCategoryID = field.NewUint64(table, "site_category_id")
|
||||
|
||||
s.fillFieldMap()
|
||||
|
||||
|
@ -88,13 +97,15 @@ func (s *site) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||
}
|
||||
|
||||
func (s *site) fillFieldMap() {
|
||||
s.fieldMap = make(map[string]field.Expr, 6)
|
||||
s.fieldMap = make(map[string]field.Expr, 8)
|
||||
s.fieldMap["id"] = s.ID
|
||||
s.fieldMap["created_at"] = s.CreatedAt
|
||||
s.fieldMap["updated_at"] = s.UpdatedAt
|
||||
s.fieldMap["deleted_at"] = s.DeletedAt
|
||||
s.fieldMap["path"] = s.Path
|
||||
s.fieldMap["advanced"] = s.Advanced
|
||||
s.fieldMap["site_category_id"] = s.SiteCategoryID
|
||||
|
||||
}
|
||||
|
||||
func (s site) clone(db *gorm.DB) site {
|
||||
|
@ -107,6 +118,77 @@ func (s site) replaceDB(db *gorm.DB) site {
|
|||
return s
|
||||
}
|
||||
|
||||
type siteBelongsToSiteCategory struct {
|
||||
db *gorm.DB
|
||||
|
||||
field.RelationField
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategory) Where(conds ...field.Expr) *siteBelongsToSiteCategory {
|
||||
if len(conds) == 0 {
|
||||
return &a
|
||||
}
|
||||
|
||||
exprs := make([]clause.Expression, 0, len(conds))
|
||||
for _, cond := range conds {
|
||||
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||
}
|
||||
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategory) WithContext(ctx context.Context) *siteBelongsToSiteCategory {
|
||||
a.db = a.db.WithContext(ctx)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategory) Session(session *gorm.Session) *siteBelongsToSiteCategory {
|
||||
a.db = a.db.Session(session)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategory) Model(m *model.Site) *siteBelongsToSiteCategoryTx {
|
||||
return &siteBelongsToSiteCategoryTx{a.db.Model(m).Association(a.Name())}
|
||||
}
|
||||
|
||||
type siteBelongsToSiteCategoryTx struct{ tx *gorm.Association }
|
||||
|
||||
func (a siteBelongsToSiteCategoryTx) Find() (result *model.SiteCategory, err error) {
|
||||
return result, a.tx.Find(&result)
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategoryTx) Append(values ...*model.SiteCategory) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Append(targetValues...)
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategoryTx) Replace(values ...*model.SiteCategory) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Replace(targetValues...)
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategoryTx) Delete(values ...*model.SiteCategory) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Delete(targetValues...)
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategoryTx) Clear() error {
|
||||
return a.tx.Clear()
|
||||
}
|
||||
|
||||
func (a siteBelongsToSiteCategoryTx) Count() int64 {
|
||||
return a.tx.Count()
|
||||
}
|
||||
|
||||
type siteDo struct{ gen.DO }
|
||||
|
||||
// FirstByID Where("id=@id")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue