优化素材中心

This commit is contained in:
xh
2025-09-16 19:40:48 +08:00
parent 60ad9b096e
commit 2736e277bb
27 changed files with 414 additions and 354 deletions

View File

@@ -15,34 +15,34 @@
"outdated": "pnpm outdated" "outdated": "pnpm outdated"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.2",
"@highlightjs/vue-plugin": "^2.1.0", "@highlightjs/vue-plugin": "^2.1.0",
"@logicflow/core": "^2.0.16", "@logicflow/core": "^2.1.2",
"@logicflow/extension": "^2.0.21", "@logicflow/extension": "^2.1.3",
"@vueuse/core": "^13.5.0", "@vueuse/core": "^13.5.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.8.4", "axios": "^1.11.0",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"css-color-function": "^1.3.3", "css-color-function": "^1.3.3",
"dayjs": "^1.11.13", "dayjs": "^1.11.18",
"echarts": "^5.6.0", "echarts": "^5.6.0",
"element-plus": "^2.10.4", "element-plus": "^2.11.1",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"query-string": "^9.1.1", "query-string": "^9.2.2",
"rolldown-vite": "^7.0.9", "rolldown-vite": "^7.1.9",
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"vform3-builds": "^3.0.10", "vform3-builds": "^3.0.10",
"vue": "^3.5.16", "vue": "^3.5.21",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
"vue-router": "^4.5.1", "vue-router": "^4.5.1",
"vue3-video-play": "^1.3.2", "vue3-video-play": "^1.3.2",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"vxe-table": "^4.14.4" "vxe-table": "^4.16.11"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.11.0", "@rushstack/eslint-patch": "^1.11.0",
@@ -50,10 +50,10 @@
"@types/node": "^22.14.1", "@types/node": "^22.14.1",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@vitejs/plugin-vue": "^6.0.0", "@vitejs/plugin-vue": "^6.0.0",
"@vitejs/plugin-vue-jsx": "^5.0.1", "@vitejs/plugin-vue-jsx": "^5.1.1",
"@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0", "@vue/eslint-config-typescript": "^13.0.0",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.8.1",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-vue": "^9.27.0", "eslint-plugin-vue": "^9.27.0",
@@ -71,7 +71,7 @@
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-style-import": "^2.0.0", "vite-plugin-style-import": "^2.0.0",
"vite-plugin-svg-icons": "^2.0.1", "vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^3.0.1" "vue-tsc": "^3.0.6"
}, },
"overrides": { "overrides": {
"vite": "npm:rolldown-vite@latest" "vite": "npm:rolldown-vite@latest"

View File

@@ -35,7 +35,7 @@
</div> </div>
</template> </template>
<script> <script lang="ts">
// import { Sketch } from 'vue-color' // import { Sketch } from 'vue-color'
// import ColorFill from './icon/ColorFill.vue' // import ColorFill from './icon/ColorFill.vue'
// import ColorText from './icon/ColorText.vue' // import ColorText from './icon/ColorText.vue'
@@ -51,7 +51,7 @@ import AreaSelect from './icon/AreaSelect.vue'
let fileHandle let fileHandle
async function getFile() { async function getFile() {
;[fileHandle] = await window.showOpenFilePicker() ;[fileHandle] = await (window as any).showOpenFilePicker()
console.log('fileHandle', fileHandle) console.log('fileHandle', fileHandle)
} }
@@ -149,6 +149,8 @@ export default {
} }
}, },
$_changeLineType(value) { $_changeLineType(value) {
console.log('value', value)
const { lf, activeEdges } = this.$props const { lf, activeEdges } = this.$props
const { graphModel } = lf const { graphModel } = lf
lf.setDefaultEdgeType(value) lf.setDefaultEdgeType(value)

View File

@@ -34,6 +34,7 @@ import UserTask from './UserTask.vue'
import FieldAuth from './FieldAuth.vue' import FieldAuth from './FieldAuth.vue'
import Gateway from './Gateway.vue' import Gateway from './Gateway.vue'
import type { NodeType, PropertiesType, FormFieldListType, FieldListType } from './property.type' import type { NodeType, PropertiesType, FormFieldListType, FieldListType } from './property.type'
defineOptions({ defineOptions({
name: 'PropertyPanel' name: 'PropertyPanel'
}) })

View File

@@ -22,13 +22,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// Importing necessary functions and components
import { ref, onMounted, useTemplateRef } from 'vue' import { ref, onMounted, useTemplateRef } from 'vue'
import LogicFlow from '@logicflow/core' import { LogicFlow } from '@logicflow/core'
import { SelectionSelect, Menu, BpmnElement, MiniMap } from '@logicflow/extension' import { SelectionSelect, Menu, BpmnElement, MiniMap } from '@logicflow/extension'
import type { NodeType, PropertiesType } from './PropertyPanel/property.type' import type { NodeType, PropertiesType } from './PropertyPanel/property.type'
// import '@logicflow/core/dist/style/index.css'
// import '@logicflow/extension/lib/style/index.css'
import '@logicflow/core/lib/style/index.css' import '@logicflow/core/lib/style/index.css'
import '@logicflow/extension/lib/style/index.css' import '@logicflow/extension/lib/style/index.css'
@@ -57,7 +56,7 @@ const props = defineProps({
}) })
// Define refs for reactive data and component references // Define refs for reactive data and component references
const lf = ref(null) // Reference to LogicFlow instance const lf = ref<LogicFlow>(null) // Reference to LogicFlow instance
const activeEdges = ref([]) // Reactive array for active edges const activeEdges = ref([]) // Reactive array for active edges
const diagramRef = useTemplateRef<HTMLInputElement>('diagramRef') // Reference to the diagram container const diagramRef = useTemplateRef<HTMLInputElement>('diagramRef') // Reference to the diagram container
const PropertyPanelRef = useTemplateRef<InstanceType<typeof PropertyPanel>>('PropertyPanelRef') // Reference to the PropertyPanel component const PropertyPanelRef = useTemplateRef<InstanceType<typeof PropertyPanel>>('PropertyPanelRef') // Reference to the PropertyPanel component
@@ -69,15 +68,9 @@ onMounted(() => {
// Function to initialize LogicFlow // Function to initialize LogicFlow
function initLogicFlow(data) { function initLogicFlow(data) {
// 引入框选插件
// LogicFlow.use(SelectionSelect)
// LogicFlow.use(Menu)
// LogicFlow.use(BpmnElement)
// LogicFlow.use(MiniMap)
// Creating a new LogicFlow instance
const logicFlowInstance = new LogicFlow({ const logicFlowInstance = new LogicFlow({
plugins: [SelectionSelect, Menu, MiniMap, BpmnElement], plugins: [SelectionSelect, Menu, MiniMap, BpmnElement],
container: diagramRef.value, // Setting the container where LogicFlow will be rendered container: diagramRef.value,
overlapMode: 1, overlapMode: 1,
// allowResize: true, // allowResize: true,
autoWrap: true, autoWrap: true,
@@ -106,7 +99,7 @@ function initLogicFlow(data) {
// Setting default edge type and rendering initial data // Setting default edge type and rendering initial data
logicFlowInstance.setDefaultEdgeType('pro-polyline') logicFlowInstance.setDefaultEdgeType('pro-polyline')
logicFlowInstance.extension.menu?.addMenuConfig({ ;(logicFlowInstance.extension.menu as Menu).addMenuConfig({
nodeMenu: [ nodeMenu: [
{ {
text: '属性配置', text: '属性配置',
@@ -117,14 +110,14 @@ function initLogicFlow(data) {
] ]
}) })
logicFlowInstance.render(data) logicFlowInstance.render(data)
logicFlowInstance.extension.miniMap?.show() ;(logicFlowInstance.extension.miniMap as MiniMap).show()
// Assigning the LogicFlow instance to the 'lf' ref // Assigning the LogicFlow instance to the 'lf' ref
lf.value = logicFlowInstance lf.value = logicFlowInstance
// Event listener for node clicks // Event listener for node clicks
lf.value.on('node:dbclick', (e) => { lf.value.on('node:dbclick', (e) => {
console.log('dbclick on node', e.data, props.fieldList) console.log('dbclick on node', e.data, props.fieldList)
PropertyPanelRef.value.open(e.data, props.fieldList) PropertyPanelRef.value.open(e.data as NodeType, props.fieldList)
}) })
} }
@@ -142,10 +135,7 @@ function setProperties(node: NodeType, item: PropertiesType) {
lf.value.setProperties(node.id, item) lf.value.setProperties(node.id, item)
} }
// function setZIndex(node, type) {
// lf.value.setElementZIndex(node.id, type)
// }
// Function to import data into the LogicFlow instance
function importData(text) { function importData(text) {
lf.value.renderRawData(text) lf.value.renderRawData(text)
} }
@@ -182,9 +172,9 @@ async function getData() {
formData: any formData: any
treeToList: any treeToList: any
}>((resolve, reject) => { }>((resolve, reject) => {
const data = lf.value.getGraphData() const data: any = lf.value.getGraphData()
const nodes = data.nodes const nodes = data?.nodes || []
const edges = data.edges const edges = data?.edges || []
let haveMoreChildNode = false let haveMoreChildNode = false
const sourceNodeIdSum = {} // Node ID -> child nodes mapping const sourceNodeIdSum = {} // Node ID -> child nodes mapping

View File

@@ -58,5 +58,6 @@ export default defineComponent({
<style> <style>
.svg-icon-container { .svg-icon-container {
display: inline-block; display: inline-block;
line-height: 1;
} }
</style> </style>

View File

@@ -1,14 +1,29 @@
<template> <template>
<div> <div>
<div class="file-item relative" :style="{ height: fileSize, width: fileSize }"> <div class="file-item relative" :style="{ height: fileSize, width: fileSize }">
<el-image class="image" v-if="type == 'image'" fit="contain" lazy :src="uri"></el-image> <el-image
<video class="video" v-else-if="type == 'video'" :src="uri"></video> class="image"
v-if="fileType == 'image'"
fit="contain"
lazy
:src="uri"
></el-image>
<video class="video" v-else-if="fileType == 'video'" :src="uri"></video>
<div <div
v-if="type == 'video'" v-if="fileType == 'video'"
class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] rounded-full w-5 h-5 flex justify-center items-center bg-[rgba(0,0,0,0.3)]" class="absolute left-1/2 top-1/2 rounded-full w-[30px] h-[30px] flex justify-center items-center bg-[rgba(0,0,0,0.3)]"
style="transform: translate(-50%, -50%)"
> >
<icon name="el-icon-CaretRight" :size="18" color="#fff" /> <icon name="el-icon-CaretRight" :size="18" color="#fff" />
</div> </div>
<div
v-if="fileType == 'audio'"
class="absolute left-1/2 top-1/2 rounded-full w-[30px] h-[30px] flex justify-center items-center bg-[rgba(0,0,0,0.3)]"
style="transform: translate(-50%, -50%)"
>
<icon name="el-icon-CaretRight" :size="16" color="#fff" />
</div>
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
@@ -16,6 +31,8 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
import { GetFileType } from '@/enums/fileEnums'
export default defineComponent({ export default defineComponent({
props: { props: {
// 图片地址 // 图片地址
@@ -35,16 +52,10 @@ export default defineComponent({
}, },
emits: ['close'], emits: ['close'],
computed: { computed: {
type() { fileType() {
const imageExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp'] const fileType = GetFileType(this.uri)
const videoExt = ['mp4', 'avi', 'mov']
if (imageExt.includes(this.ext)) { return fileType
return 'image'
}
if (videoExt.includes(this.ext)) {
return 'video'
}
return 'file'
} }
} }
}) })

View File

@@ -85,8 +85,8 @@ export function useCate() {
// 处理文件的钩子函数 // 处理文件的钩子函数
export function useFile( export function useFile(
cateId: Ref<string | number>, cateId: Ref<string | number>,
ext: string[], ext: Ref<string[]>,
limit: Ref<number>, limit: number,
size: number size: number
) { ) {
const tableRef = shallowRef() const tableRef = shallowRef()
@@ -99,6 +99,7 @@ export function useFile(
ext: ext, ext: ext,
cid: cateId cid: cateId
}) })
const { pager, getLists, resetPage } = usePaging({ const { pager, getLists, resetPage } = usePaging({
fetchFun: fileList, fetchFun: fileList,
params: fileParams, params: fileParams,
@@ -141,8 +142,8 @@ export function useFile(
select.value.splice(index, 1) select.value.splice(index, 1)
return return
} }
if (select.value.length == limit.value) { if (select.value.length == limit) {
if (limit.value == 1) { if (limit == 1) {
select.value = [] select.value = []
select.value.push(item) select.value.push(item)
return return
@@ -157,7 +158,7 @@ export function useFile(
select.value = [] select.value = []
} }
const cancelSelete = (id: number) => { const cancelSelect = (id: number) => {
select.value = select.value.filter((item: any) => item.id != id) select.value = select.value.filter((item: any) => item.id != id)
} }
const selectItems = (items: any[]) => { const selectItems = (items: any[]) => {
@@ -194,7 +195,7 @@ export function useFile(
batchFileMove, batchFileMove,
selectFile, selectFile,
clearSelect, clearSelect,
cancelSelete, cancelSelect,
selectAll, selectAll,
selectItems, selectItems,
handleFileRename handleFileRename

View File

@@ -32,7 +32,7 @@
v-if="data.id > 0" v-if="data.id > 0"
:hide-on-click="false" :hide-on-click="false"
> >
<span class="muted m-r-10">···</span> <span class="p-1 mr-[5px]">···</span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<popover-input <popover-input
@@ -67,34 +67,37 @@
</el-scrollbar> </el-scrollbar>
</div> </div>
<div class="flex justify-center p-2 border-t border-br"> <div class="flex justify-center pt-2 border-t border-br">
<popover-input <popover-input
v-perms="['admin:common:album:cateAdd']" v-perms="['admin:common:album:cateAdd']"
@confirm="handleAddCate" @confirm="handleAddCate"
size="default" size="default"
width="400px" width="500px"
:limit="20" :limit="20"
show-limit show-limit
teleported teleported
> >
<el-button> 添加分组 </el-button> <el-button>添加分组</el-button>
</popover-input> </popover-input>
</div> </div>
</div> </div>
<div class="material__center flex flex-col"> <div class="material__center flex flex-col">
<div class="operate-btn flex"> <el-tabs
<div class="flex-1 flex"> v-if="mode == 'page' && defaultFileType == 'all'"
<upload v-model="activeFileType"
v-perms="['admin:common:upload:image']" @tab-change="handleTabChange"
class="mr-3"
:data="{ cid: cateId }"
:ext="ext"
:show-progress="true"
@change="refresh"
> >
<el-button type="primary">本地上传</el-button> <el-tab-pane
</upload> v-for="item in FileTabsMap"
</div> :label="item.name"
:name="item.fileType"
:key="item.fileType"
>
</el-tab-pane>
</el-tabs>
<div class="flex flex-col">
<div class="operate-btn flex">
<div class="flex-1 flex"></div>
<el-input <el-input
class="w-60" class="w-60"
placeholder="请输入名称" placeholder="请输入名称"
@@ -109,6 +112,17 @@
</el-button> </el-button>
</template> </template>
</el-input> </el-input>
<upload
v-perms="['admin:common:upload:file']"
class="ml-3"
:data="{ cid: cateId }"
:ext="ext"
:show-progress="true"
@change="refresh"
>
<el-button type="primary">本地上传</el-button>
</upload>
</div>
</div> </div>
<div class="material-center__content flex flex-col flex-1 mb-1 min-h-0"> <div class="material-center__content flex flex-col flex-1 mb-1 min-h-0">
@@ -127,7 +141,6 @@
<file-item <file-item
:uri="row.uri" :uri="row.uri"
file-size="50px" file-size="50px"
:ext="row.ext"
@click.stop="handlePreview(row.uri)" @click.stop="handlePreview(row.uri)"
></file-item> ></file-item>
</template> </template>
@@ -140,9 +153,9 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="大小" prop="size" min-width="100"> </el-table-column> <el-table-column label="大小" prop="size" min-width="100"> </el-table-column>
<el-table-column label="格式" prop="ext" min-width="100"></el-table-column> <el-table-column label="格式" prop="ext" min-width="80"></el-table-column>
<el-table-column prop="createTime" label="上传时间" min-width="100" /> <el-table-column prop="createTime" label="上传时间" min-width="160" />
<el-table-column label="操作" width="150" fixed="right"> <el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<div class="inline-block" v-perms="['admin:common:album:albumRename']"> <div class="inline-block" v-perms="['admin:common:album:albumRename']">
@@ -150,7 +163,7 @@
@confirm="handleFileRename($event, row.id)" @confirm="handleFileRename($event, row.id)"
size="default" size="default"
:value="row.name" :value="row.name"
width="400px" width="500px"
:limit="50" :limit="50"
show-limit show-limit
teleported teleported
@@ -175,27 +188,10 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div
class="flex flex-1 justify-center items-center"
v-if="!pager.loading && !pager.lists.length"
>
暂无数据~
</div>
</div> </div>
<div class="material-center__footer flex justify-between items-center mt-2"> <div class="material-center__footer flex justify-between items-center mt-2">
<div class="flex"> <div class="flex">
<template v-if="mode == 'page'"> <template v-if="mode == 'page'">
<!-- <span class="mr-3">
<el-checkbox
:disabled="!pager.lists.length"
v-model="isCheckAll"
@change="selectAll"
:indeterminate="isIndeterminate"
>
当页全选
</el-checkbox>
</span> -->
<el-button <el-button
v-perms="['admin:common:album:albumDel']" v-perms="['admin:common:album:albumDel']"
:disabled="!select.length" :disabled="!select.length"
@@ -206,9 +202,9 @@
<popup <popup
v-perms="['admin:common:album:albumMove']" v-perms="['admin:common:album:albumMove']"
class="ml-3 inline" class="ml-3 inline"
@confirm="batchFileMove"
:disabled="!select.length" :disabled="!select.length"
title="移动文件" title="移动文件"
@confirm="batchFileMove"
> >
<template #trigger> <template #trigger>
<el-button :disabled="!select.length">移动</el-button> <el-button :disabled="!select.length">移动</el-button>
@@ -246,12 +242,8 @@
<ul class="select-lists flex flex-col p-t-3"> <ul class="select-lists flex flex-col p-t-3">
<li class="mb-4" v-for="item in select" :key="item.id"> <li class="mb-4" v-for="item in select" :key="item.id">
<div class="select-item"> <div class="select-item">
<del-wrap @close="cancelSelete(item.id)"> <del-wrap @close="cancelSelect(item.id)">
<file-item <file-item :uri="item.uri" file-size="100px"></file-item>
:uri="item.uri"
file-size="100px"
:ext="item.ext"
></file-item>
</del-wrap> </del-wrap>
</div> </div>
</li> </li>
@@ -264,29 +256,22 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, toRefs, ref, watch } from 'vue' import { FileTabsMap } from '@/enums/fileEnums'
import type { PropType } from 'vue' import { onMounted, ref, watch, computed } from 'vue'
import { FileExt } from '@/enums/fileEnums'
import { useCate, useFile } from './hook' import { useCate, useFile } from './hook'
import FileItem from './file.vue' import FileItem from './file.vue'
import Preview from './preview.vue' import Preview from './preview.vue'
const props = defineProps({ const props = defineProps({
fileSize: {
type: String,
default: '100px'
},
limit: { limit: {
type: Number, type: Number,
default: 1 default: 1
}, },
ext: { defaultFileType: {
type: Array as PropType<string[]>, type: String,
default: () => [] default: 'all'
}, },
// type: {
// type: String,
// default: 'image'
// },
mode: { mode: {
type: String, type: String,
default: 'picker' default: 'picker'
@@ -297,20 +282,8 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['change']) const emit = defineEmits(['change'])
const { limit } = toRefs(props) // const { limit } = toRefs(props)
// const typeValue = computed<number>(() => {
// switch (props.type) {
// case 'image':
// return 10
// case 'video':
// return 20
// case 'file':
// return 30
// default:
// return 0
// }
// })
// const visible: Ref<boolean> = inject('visible')
const previewUrl = ref('') const previewUrl = ref('')
const showPreview = ref(false) const showPreview = ref(false)
const { const {
@@ -324,6 +297,23 @@ const {
handleCatSelect handleCatSelect
} = useCate() } = useCate()
const activeFileType = ref(props.defaultFileType)
const ext = computed(() => {
if (activeFileType.value) {
return FileExt[activeFileType.value]
}
return []
})
const listExt = computed(() => {
if (activeFileType.value == 'all') {
return []
}
if (activeFileType.value) {
return FileExt[activeFileType.value]
}
return []
})
const { const {
tableRef, tableRef,
moveId, moveId,
@@ -338,15 +328,23 @@ const {
batchFileMove, batchFileMove,
clearSelect, clearSelect,
cancelSelete, cancelSelect,
selectItems, selectItems,
handleFileRename handleFileRename
} = useFile(cateId, props.ext, limit, props.pageSize) } = useFile(cateId, listExt, props.limit, props.pageSize)
function handleSelectionChange(val: any[]) { function handleSelectionChange(val: any[]) {
console.log('handleSelectionChange', val) // console.log('handleSelectionChange', val)
// if (props.limit && val.length > props.limit) {
// return false
// }
selectItems(val) selectItems(val)
// multipleSelection.value = val }
const handleTabChange = () => {
// activeFileType.value = tab
console.log('handleTabChange', activeFileType.value)
getFileList()
} }
const getData = async () => { const getData = async () => {
await getCateLists() await getCateLists()
@@ -386,7 +384,8 @@ watch(
) )
onMounted(() => { onMounted(() => {
props.mode == 'page' && getData() // props.mode == 'page' && getData()
getData()
}) })
defineExpose({ defineExpose({

View File

@@ -1,8 +1,8 @@
<template> <template>
<div class="material-select"> <div class="material-picker">
<popup <popup
ref="popupRef" ref="popupRef"
width="830px" width="1200px"
custom-class="body-padding" custom-class="body-padding"
:title="`选择${tipsText}`" :title="`选择${tipsText}`"
@confirm="handleConfirm" @confirm="handleConfirm"
@@ -24,7 +24,6 @@
<file-item <file-item
:uri="excludeDomain ? getImageUrl(element) : element" :uri="excludeDomain ? getImageUrl(element) : element"
:file-size="size" :file-size="size"
:type="type"
></file-item> ></file-item>
</del-wrap> </del-wrap>
<div class="operation-btns text-xs text-center"> <div class="operation-btns text-xs text-center">
@@ -61,21 +60,21 @@
</div> </div>
</div> </div>
</template> </template>
<el-scrollbar>
<div class="material-wrap"> <div class="material-wrap">
<material <material
ref="materialRef" ref="materialRef"
mode="page" mode="picker"
:type="type" defaultFileType="image"
:ext="ext"
:file-size="fileSize" :file-size="fileSize"
:limit="meterialLimit" :limit="materialLimit"
@change="selectChange" @change="selectChange"
/> />
</div> </div>
</el-scrollbar>
</popup> </popup>
<preview v-model="showPreview" :url="previewUrl" :type="type" /> <preview v-model="showPreview" :url="previewUrl" />
</div> </div>
</template> </template>
@@ -88,6 +87,8 @@ import Material from './index.vue'
import Preview from './preview.vue' import Preview from './preview.vue'
import useAppStore from '@/stores/modules/app' import useAppStore from '@/stores/modules/app'
import { useThrottleFn } from '@vueuse/core' import { useThrottleFn } from '@vueuse/core'
import { FileExt } from '@/enums/fileEnums'
export default defineComponent({ export default defineComponent({
components: { components: {
Popup, Popup,
@@ -154,6 +155,10 @@ export default defineComponent({
const currentIndex = ref(-1) const currentIndex = ref(-1)
const { disabled, limit, modelValue } = toRefs(props) const { disabled, limit, modelValue } = toRefs(props)
const { getImageUrl } = useAppStore() const { getImageUrl } = useAppStore()
const ext = computed(() => {
return FileExt[props.type]
})
const tipsText = computed(() => { const tipsText = computed(() => {
switch (props.type) { switch (props.type) {
case 'image': case 'image':
@@ -168,7 +173,7 @@ export default defineComponent({
const showUpload = computed(() => { const showUpload = computed(() => {
return props.limit - fileList.value.length > 0 return props.limit - fileList.value.length > 0
}) })
const meterialLimit: any = computed(() => { const materialLimit: any = computed(() => {
if (!isAdd.value) { if (!isAdd.value) {
return 1 return 1
} }
@@ -240,12 +245,13 @@ export default defineComponent({
provide('limit', props.limit) provide('limit', props.limit)
provide('hiddenUpload', props.hiddenUpload) provide('hiddenUpload', props.hiddenUpload)
return { return {
ext,
popupRef, popupRef,
materialRef, materialRef,
fileList, fileList,
tipsText, tipsText,
handleConfirm, handleConfirm,
meterialLimit, materialLimit,
showUpload, showUpload,
showPopup, showPopup,
selectChange, selectChange,
@@ -261,7 +267,7 @@ export default defineComponent({
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.material-select { .material-picker {
.material-upload, .material-upload,
.material-preview { .material-preview {
position: relative; position: relative;
@@ -301,7 +307,7 @@ export default defineComponent({
} }
.material-wrap { .material-wrap {
min-width: 720px; min-width: 720px;
height: 430px; height: calc(100vh - 170px);
@apply border-t border-b border-br; border-top: 1px var(--el-border-color) solid;
} }
</style> </style>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div v-show="modelValue"> <div v-show="modelValue">
<div v-if="type == 'image'"> <div v-if="fileType == 'image'">
<el-image-viewer <el-image-viewer
v-if="previewLists.length" v-if="previewLists.length"
:url-list="previewLists" :url-list="previewLists"
@@ -8,8 +8,8 @@
@close="handleClose" @close="handleClose"
/> />
</div> </div>
<div v-if="type == 'video'"> <div v-if="fileType == 'video' || fileType == 'audio'">
<el-dialog v-model="visible" width="740px" title="视频预览" :before-close="handleClose"> <el-dialog v-model="visible" width="900px" title="视频预览" :before-close="handleClose">
<video-player ref="playerRef" :src="url" width="100%" height="450px" /> <video-player ref="playerRef" :src="url" width="100%" height="450px" />
</el-dialog> </el-dialog>
</div> </div>
@@ -18,6 +18,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, useTemplateRef, watch, computed, nextTick } from 'vue' import { ref, useTemplateRef, watch, computed, nextTick } from 'vue'
import { GetFileType } from '@/enums/fileEnums'
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Boolean, type: Boolean,
@@ -33,17 +35,8 @@ const props = defineProps({
// } // }
}) })
const type = computed(() => { const fileType = computed(() => {
const imageExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp'] return GetFileType(props.url)
const videoExt = ['mp4', 'avi', 'mov']
const ext = props.url.split('.').pop()
if (imageExt.includes(ext)) {
return 'image'
}
if (videoExt.includes(ext)) {
return 'video'
}
return 'file'
}) })
const playerRef = useTemplateRef('playerRef') const playerRef = useTemplateRef('playerRef')

View File

@@ -14,7 +14,7 @@
:on-error="handleError" :on-error="handleError"
:accept="getAccept" :accept="getAccept"
> >
<slot></slot>{{ getAccept }} <slot></slot>
</el-upload> </el-upload>
<el-dialog <el-dialog
v-if="showProgress && fileList.length" v-if="showProgress && fileList.length"
@@ -59,11 +59,6 @@ export default defineComponent({
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
default: () => [] default: () => []
}, },
// 上传文件类型
type: {
type: String,
default: 'image'
},
// 是否支持多选 // 是否支持多选
multiple: { multiple: {
type: Boolean, type: Boolean,

View File

@@ -36,7 +36,7 @@ const options = reactive({
loop: false, //循环播放 loop: false, //循环播放
mirror: false, //镜像画面 mirror: false, //镜像画面
ligthOff: false, //关灯模式 ligthOff: false, //关灯模式
volume: 0.3, //默认音量大小 volume: 1, //默认音量大小
control: true, //是否显示控制器 control: true, //是否显示控制器
title: '', //视频名称 title: '', //视频名称
poster: '', //封面 poster: '', //封面
@@ -59,7 +59,7 @@ const onPause = (event: any) => {
} }
const onTimeupdate = (event: any) => { const onTimeupdate = (event: any) => {
console.log(event, '时间更新') // console.log(event, '时间更新')
} }
const onCanplay = (event: any) => { const onCanplay = (event: any) => {
console.log(event, '可以播放') console.log(event, '可以播放')

View File

@@ -0,0 +1,87 @@
export const imageExt = ['png', 'jpg', 'jpeg', 'gif', 'ico', 'bmp', 'webp', 'avif']
export const videoExt = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'rmvb']
export const audioExt = ['mp3', 'wav', 'aac', 'flac']
export const officeExt = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'txt']
export const fileExt = ['zip', '7z', 'rar']
export const All_EXT = [...imageExt, ...videoExt, ...audioExt, ...fileExt]
// 获取文件类型
export function GetFileType(url: string) {
const ext = url.split('.').pop()
if (officeExt.includes(ext)) {
return 'office'
}
if (audioExt.includes(ext)) {
return 'audio'
}
if (imageExt.includes(ext)) {
return 'image'
}
if (videoExt.includes(ext)) {
return 'video'
}
return 'file'
}
// 获取文件类型上传的accept
function getAccept(ext: string[]) {
return ext
.map((item) => {
return `.${item}`
})
.join(',')
}
export const FileTabsMap = [
{
name: '全部',
fileType: 'all',
accept: getAccept(All_EXT),
ext: []
},
{
name: '图片',
fileType: 'image',
accept: getAccept(imageExt),
ext: imageExt
},
{
name: '视频',
fileType: 'video',
accept: getAccept(videoExt),
ext: videoExt
},
{
name: '音频',
fileType: 'audio',
accept: getAccept(audioExt),
ext: audioExt
},
{
name: '文档',
fileType: 'office',
accept: getAccept(officeExt),
ext: officeExt
},
{
name: '文件',
fileType: 'file',
accept: getAccept(fileExt),
ext: fileExt
}
]
export const FileExt = {
all: All_EXT,
image: imageExt,
video: videoExt,
audio: audioExt,
office: officeExt,
file: fileExt
}
export const FileAccept = {
all: getAccept(All_EXT),
image: getAccept(imageExt),
video: getAccept(videoExt),
audio: getAccept(audioExt),
office: getAccept(officeExt),
file: getAccept(fileExt)
}

View File

@@ -8,6 +8,7 @@ export interface FileUploaderOptions {
chunkSize?: number chunkSize?: number
onSuccess?: (filePath: string) => void onSuccess?: (filePath: string) => void
onError?: (error: Error) => void onError?: (error: Error) => void
onCalculateMD5Progress?: (percent: number) => void
onUploadProgress?: ( onUploadProgress?: (
chunkIndex: number, chunkIndex: number,
chunkCount: number, chunkCount: number,
@@ -43,6 +44,10 @@ export default class FileUploader {
this.startChunkIndex = -1 this.startChunkIndex = -1
this.onError(error) this.onError(error)
} }
calculateMD5Progress(chunkIndex: number, chunkTotal: number) {
const chunkPercent = Math.floor((chunkIndex / chunkTotal) * 100)
this.onCalculateMD5Progress(chunkPercent)
}
uploadProgress(chunkIndex: number, chunkLoaded: number, chunkTotal: number) { uploadProgress(chunkIndex: number, chunkLoaded: number, chunkTotal: number) {
// 计算百分比 // 计算百分比
const chunkPercent = Math.floor((chunkLoaded / chunkTotal) * 100) const chunkPercent = Math.floor((chunkLoaded / chunkTotal) * 100)
@@ -52,6 +57,7 @@ export default class FileUploader {
onChunkSuccess: FileUploaderOptions['onChunkSuccess'] = function () {} onChunkSuccess: FileUploaderOptions['onChunkSuccess'] = function () {}
onChunkError: FileUploaderOptions['onChunkError'] = function () {} onChunkError: FileUploaderOptions['onChunkError'] = function () {}
onError: FileUploaderOptions['onError'] = function () {} onError: FileUploaderOptions['onError'] = function () {}
onCalculateMD5Progress: FileUploaderOptions['onCalculateMD5Progress'] = function () {}
onUploadProgress: FileUploaderOptions['onUploadProgress'] = function () {} onUploadProgress: FileUploaderOptions['onUploadProgress'] = function () {}
/** /**
@@ -103,21 +109,21 @@ export default class FileUploader {
this.fileSize = file.size this.fileSize = file.size
this.chunkCount = Math.ceil(this.file.size / this.chunkSize) this.chunkCount = Math.ceil(this.file.size / this.chunkSize)
} }
readerFile(file: File): Promise<ArrayBuffer> { // readerFile(file: File): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
if (!file) { // if (!file) {
return reject(new Error('读取文件失败')) // return reject(new Error('读取文件失败'))
} // }
const reader = new FileReader() // const reader = new FileReader()
reader.onload = (e) => { // reader.onload = (e) => {
resolve(e.target?.result as ArrayBuffer) // resolve(e.target?.result as ArrayBuffer)
} // }
reader.onerror = (e) => { // reader.onerror = (e) => {
reject(e) // reject(e)
} // }
reader.readAsArrayBuffer(file) // reader.readAsArrayBuffer(file)
}) // })
} // }
getAbortControllerSignal() { getAbortControllerSignal() {
const controller = new AbortController() const controller = new AbortController()
this.abortControllers.push(controller) this.abortControllers.push(controller)
@@ -148,8 +154,10 @@ export default class FileUploader {
} }
this.uploading = true this.uploading = true
const arrayBuffer = await this.readerFile(this.file) // const arrayBuffer = await this.readerFile(this.file)
const fileMd5 = this.getMd5(arrayBuffer) console.time('SparkMD5')
const fileMd5 = await this.calculateMD5(this.file)
console.timeEnd('SparkMD5')
this.fileMd5 = fileMd5 + '_' + this.fileSize this.fileMd5 = fileMd5 + '_' + this.fileSize
const isExistFilePath = await this.checkFileExist() const isExistFilePath = await this.checkFileExist()
@@ -167,19 +175,58 @@ export default class FileUploader {
this.error(error) this.error(error)
} }
} }
/**
// 检查上传状态 * 计算文件的MD5值
getMd5(arrayBuffer: ArrayBuffer): string { * @param file
if (!this.uploading) { * @returns
return */
} async calculateMD5(file: File) {
console.time('SparkMD5') return new Promise((resolve, reject) => {
const spark = new SparkMD5.ArrayBuffer() const spark = new SparkMD5.ArrayBuffer()
spark.append(arrayBuffer) const reader = new FileReader()
const hash = spark.end() const chunkSize = 10 * 1024 * 1024 // 10MB 分块
console.timeEnd('SparkMD5') let currentChunk = 0
return hash const chunks = Math.ceil(file.size / chunkSize)
reader.onload = function (e) {
spark.append(e.target.result) // 添加数组缓冲区
currentChunk++
if (currentChunk < chunks) {
loadNext()
} else {
resolve(spark.end())
} }
}
reader.onerror = function () {
reject('文件读取错误')
}
function loadNext() {
console.log('md5进度', currentChunk, chunks)
this.calculateMD5Progress(currentChunk, chunks)
const start = currentChunk * chunkSize
const end = Math.min(start + chunkSize, file.size)
reader.readAsArrayBuffer(file.slice(start, end))
}
loadNext()
})
}
// 计算文件的MD5值
// getMd5(arrayBuffer: ArrayBuffer): string {
// if (!this.uploading) {
// return
// }
// console.time('SparkMD5')
// const spark = new SparkMD5.ArrayBuffer()
// spark.append(arrayBuffer)
// const hash = spark.end()
// console.timeEnd('SparkMD5')
// return hash
// }
// 检查文件是否存在,可实现秒传 // 检查文件是否存在,可实现秒传
async checkFileExist(): Promise<string> { async checkFileExist(): Promise<string> {
if (!this.uploading) { if (!this.uploading) {

View File

@@ -1,72 +1,22 @@
<template> <template>
<div class="material-index"> <div class="material-index">
<el-card class="!border-none" shadow="never"> <MaterialComponent mode="page" :limit="-1" :page-size="10" />
<el-tabs v-model="activeTab">
<el-tab-pane
v-for="item in tabsMap"
:label="item.name"
:name="item.name"
:index="item.name"
:key="item.name"
lazy
>
<material
:ext="item.ext"
mode="page"
file-size="120px"
:limit="-1"
:page-size="10"
/>
</el-tab-pane>
</el-tabs>
</el-card>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import MaterialComponent from '@/components/material/index.vue'
import material from '@/components/material/index.vue'
defineOptions({ defineOptions({
name: 'materialCenter' name: 'materialCenter'
}) })
const tabsMap = [
{
// type: '',
name: '全部',
ext: []
},
{
// type: 'image',
name: '图片',
ext: ['jpg', 'jpeg', 'png', 'gif', 'bmp']
},
{
// type: 'video',
name: '视频',
ext: ['mp4', 'avi', 'mov']
},
{
// type: 'pdf',
name: 'pdf',
ext: ['pdf']
}
]
const activeTab = ref('image')
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.material-index { .material-index {
min-width: 700px; height: calc(100vh - 135px);
:deep(.el-tabs) { overflow: hidden;
height: calc(100vh - 180px); background-color: white;
padding: 0 5px 5px;
.el-tabs__content,
.el-tab-pane {
min-height: 0;
flex: 1;
display: flex;
flex-direction: column;
}
}
} }
</style> </style>

View File

@@ -65,42 +65,6 @@
</el-card> </el-card>
<el-card class="!border-none mt-4" shadow="never"> <el-card class="!border-none mt-4" shadow="never">
<div class="text-right"> <div class="text-right">
<!-- <el-button
v-perms="['admin:monitor_client:add']"
type="primary"
@click="handleAdd()"
>
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增
</el-button>
<upload
v-perms="['admin:monitor_client:ImportFile']"
class="ml-3 mr-3"
:url="monitor_client_import_file"
:data="{ cid: 0 }"
type="file"
:show-progress="true"
@change="resetPage"
>
<el-button type="primary">
<template #icon>
<icon name="el-icon-Upload" />
</template>
导入
</el-button>
</upload>
<el-button
v-perms="['admin:monitor_client:ExportFile']"
type="primary"
@click="exportFile"
>
<template #icon>
<icon name="el-icon-Download" />
</template>
导出
</el-button> -->
<el-button <el-button
v-perms="['admin:monitor_client:delBatch']" v-perms="['admin:monitor_client:delBatch']"
type="danger" type="danger"

View File

@@ -72,7 +72,7 @@
class="ml-3 mr-3" class="ml-3 mr-3"
:url="monitor_project_import_file" :url="monitor_project_import_file"
:data="{ cid: 0 }" :data="{ cid: 0 }"
type="file" :ext="['xlsx']"
:show-progress="true" :show-progress="true"
@change="resetPage" @change="resetPage"
> >

View File

@@ -37,7 +37,7 @@
class="ml-3 mr-3" class="ml-3 mr-3"
:url="adminImportFile" :url="adminImportFile"
:data="{ cid: 0 }" :data="{ cid: 0 }"
type="file" :ext="['xlsx']"
:show-progress="true" :show-progress="true"
@change="resetPage" @change="resetPage"
> >

View File

@@ -47,7 +47,7 @@
class="ml-3 mr-3" class="ml-3 mr-3"
:url="user_protocol_import_file" :url="user_protocol_import_file"
:data="{ cid: 0 }" :data="{ cid: 0 }"
type="file" :ext="['xlsx']"
:show-progress="true" :show-progress="true"
@change="resetPage" @change="resetPage"
> >

View File

@@ -69,11 +69,14 @@
/> />
</div> </div>
</el-form-item> </el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSubmit">保存</el-button>
</el-form-item>
</el-form> </el-form>
</el-card> </el-card>
<footer-btns> <!-- <footer-btns>
<el-button type="primary" @click="handleSubmit">保存</el-button> <el-button type="primary" @click="handleSubmit">保存</el-button>
</footer-btns> </footer-btns> -->
</div> </div>
</template> </template>
@@ -105,7 +108,7 @@ const rules = reactive<object>({
{ {
required: true, required: true,
message: '头像不能为空', message: '头像不能为空',
trigger: ['change'] trigger: ['blur']
} }
], ],
nickname: [ nickname: [

View File

@@ -20,13 +20,13 @@ var FileConfig = fileConfig{
PublicPrefix: "/api/uploads", PublicPrefix: "/api/uploads",
// 上传文件路径 // 上传文件路径
UploadDirectory: "/tmp/uploads/x_admin_go/", UploadDirectory: "/tmp/uploads/x_admin_go/",
UploadImageSize: 10 * 1024 * 1024, // 10MB UploadImageSize: 20 * 1024 * 1024, // 20MB
UploadVideoSize: 500 * 1024 * 1024, // 500MB UploadVideoSize: 2000 * 1024 * 1024, // 2000MB
UploadFileSize: 1024 * 1024 * 1024, //1GB UploadFileSize: 1024 * 1024 * 1024, //1GB
// 上传图片扩展 // 上传图片扩展
UploadImageExt: []string{"png", "jpg", "jpeg", "gif", "ico", "bmp", "webp", "avif"}, UploadImageExt: []string{"png", "jpg", "jpeg", "gif", "ico", "bmp", "webp", "avif"},
// 上传视频扩展 // 上传视频扩展
UploadVideoExt: []string{"mp4", "mp3", "avi", "flv", "rmvb", "mov"}, UploadVideoExt: []string{"mp4", "avi", "flv", "wmv", "rmvb", "mov", "mp3", "wav", "flac", "m4a"},
UploadFileExt: []string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "zip", "rar", "7z", "txt"}, UploadFileExt: []string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "zip", "rar", "7z", "txt"},
} }

View File

@@ -11,7 +11,7 @@ type UserProtocol struct {
Id int `gorm:"primarykey;comment:''"` // Id int `gorm:"primarykey;comment:''"` //
Title string `gorm:"comment:'标题'"` // 标题 Title string `gorm:"comment:'标题'"` // 标题
Content string `gorm:"comment:'协议内容'"` // 协议内容 Content string `gorm:"comment:'协议内容'"` // 协议内容
Sort core.NullFloat `gorm:"comment:'排序'"` // 排序 // Sort core.NullFloat `gorm:"comment:'排序'"` // 排序
IsDelete soft_delete.DeletedAt `gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"` IsDelete soft_delete.DeletedAt `gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"`
CreateTime core.NullTime `gorm:"autoCreateTime;comment:'创建时间'"` // 创建时间 CreateTime core.NullTime `gorm:"autoCreateTime;comment:'创建时间'"` // 创建时间
UpdateTime core.NullTime `gorm:"autoUpdateTime;comment:'更新时间'"` // 更新时间 UpdateTime core.NullTime `gorm:"autoUpdateTime;comment:'更新时间'"` // 更新时间

View File

@@ -134,7 +134,7 @@ func (sd storageDriver) checkFile(fileName string, fileSize int64) (e error) {
} else if util.ToolsUtil.Contains(config.FileConfig.UploadVideoExt, fileExt) { } else if util.ToolsUtil.Contains(config.FileConfig.UploadVideoExt, fileExt) {
// 视频文件 // 视频文件
if fileSize > config.FileConfig.UploadVideoSize { if fileSize > config.FileConfig.UploadVideoSize {
return response.Failed.SetMessage("上传视频不能超出限制: " + strconv.FormatInt(config.FileConfig.UploadVideoSize/1024/1024, 10) + "M") return response.Failed.SetMessage("上传视频不能超出限制: " + strconv.FormatInt(config.FileConfig.UploadVideoSize/1024/1024, 10) + "M")
} }
} else if util.ToolsUtil.Contains(config.FileConfig.UploadFileExt, fileExt) { } else if util.ToolsUtil.Contains(config.FileConfig.UploadFileExt, fileExt) {
// 文件 // 文件

View File

@@ -8,7 +8,7 @@ import (
type UserProtocolListReq struct { type UserProtocolListReq struct {
Title *string // 标题 Title *string // 标题
Content *string // 协议内容 Content *string // 协议内容
Sort *float64 // 排序 // Sort *float64 // 排序
CreateTimeStart *string // 开始创建时间 CreateTimeStart *string // 开始创建时间
CreateTimeEnd *string // 结束创建时间 CreateTimeEnd *string // 结束创建时间
UpdateTimeStart *string // 开始更新时间 UpdateTimeStart *string // 开始更新时间
@@ -19,7 +19,7 @@ type UserProtocolListReq struct {
type UserProtocolAddReq struct { type UserProtocolAddReq struct {
Title *string // 标题 Title *string // 标题
Content *string // 协议内容 Content *string // 协议内容
Sort core.NullFloat // 排序 // Sort core.NullFloat // 排序
} }
// UserProtocolEditReq 用户协议编辑参数 // UserProtocolEditReq 用户协议编辑参数
@@ -27,7 +27,7 @@ type UserProtocolEditReq struct {
Id int // Id int //
Title *string // 标题 Title *string // 标题
Content *string // 协议内容 Content *string // 协议内容
Sort core.NullFloat // 排序 // Sort core.NullFloat // 排序
} }
// UserProtocolDetailReq 用户协议详情参数 // UserProtocolDetailReq 用户协议详情参数
@@ -50,7 +50,7 @@ type UserProtocolResp struct {
Id int // Id int //
Title string // 标题 Title string // 标题
Content string // 协议内容 Content string // 协议内容
Sort core.NullFloat // 排序 // Sort core.NullFloat // 排序
CreateTime core.NullTime // 创建时间 CreateTime core.NullTime // 创建时间
UpdateTime core.NullTime // 更新时间 UpdateTime core.NullTime // 更新时间
} }

View File

@@ -48,9 +48,6 @@ func (albSrv albumService) AlbumList(adminId uint, page request.PageReq, listReq
albumModel = albumModel.Where("ext in ?", listReq.Ext) albumModel = albumModel.Where("ext in ?", listReq.Ext)
} }
// if listReq.Type > 0 {
// albumModel = albumModel.Where("type = ?", listReq.Type)
// }
// 总数 // 总数
var count int64 var count int64
err := albumModel.Count(&count).Error err := albumModel.Count(&count).Error
@@ -134,7 +131,7 @@ func (albSrv albumService) AlbumAdd(addReq commonSchema.CommonAlbumAddReq) (res
//} //}
convert_util.Copy(&alb, addReq) convert_util.Copy(&alb, addReq)
err := albSrv.db.Create(&alb).Error err := albSrv.db.Create(&alb).Error
if e = response.CheckErr(err, "Album添加失败"); e != nil { if e = response.CheckErr(err, "相册添加失败"); e != nil {
return return
} }
return alb.ID, nil return alb.ID, nil
@@ -144,7 +141,7 @@ func (albSrv albumService) AlbumAdd(addReq commonSchema.CommonAlbumAddReq) (res
func (albSrv albumService) AlbumDel(ids []uint) (e error) { func (albSrv albumService) AlbumDel(ids []uint) (e error) {
var albums []common_model.Album var albums []common_model.Album
err := albSrv.db.Where("id in ? AND is_delete = ?", ids, 0).Find(&albums).Error err := albSrv.db.Where("id in ? AND is_delete = ?", ids, 0).Find(&albums).Error
if e = response.CheckErr(err, "AlbumDel Find err"); e != nil { if e = response.CheckErr(err, "相册文件查找失败"); e != nil {
return return
} }
if len(albums) == 0 { if len(albums) == 0 {
@@ -152,7 +149,7 @@ func (albSrv albumService) AlbumDel(ids []uint) (e error) {
} }
err = albSrv.db.Model(&common_model.Album{}).Where("id in ?", ids).Updates( err = albSrv.db.Model(&common_model.Album{}).Where("id in ?", ids).Updates(
common_model.Album{IsDelete: 1, DeleteTime: util.NullTimeUtil.Now()}).Error common_model.Album{IsDelete: 1, DeleteTime: util.NullTimeUtil.Now()}).Error
e = response.CheckErr(err, "AlbumDel UpdateColumn err") e = response.CheckErr(err, "相册文件删除失败")
return return
} }
@@ -169,7 +166,7 @@ func (albSrv albumService) CateList(adminId uint, listReq commonSchema.CommonCat
cateModel = cateModel.Where("name like ?", "%"+listReq.Name+"%") cateModel = cateModel.Where("name like ?", "%"+listReq.Name+"%")
} }
err := cateModel.Find(&cates).Error err := cateModel.Find(&cates).Error
if e = response.CheckErr(err, "Cate列表获取失败"); e != nil { if e = response.CheckErr(err, "分类列表获取失败"); e != nil {
return return
} }
cateResps := []commonSchema.CommonCateListResp{} cateResps := []commonSchema.CommonCateListResp{}
@@ -181,11 +178,17 @@ func (albSrv albumService) CateList(adminId uint, listReq commonSchema.CommonCat
func (albSrv albumService) CateAdd(adminId uint, addReq commonSchema.CommonCateAddReq) (e error) { func (albSrv albumService) CateAdd(adminId uint, addReq commonSchema.CommonCateAddReq) (e error) {
var cate common_model.AlbumCate var cate common_model.AlbumCate
// 查询分类是否存在
albSrv.db.Where("admin_id = ? AND pid=? AND name = ? AND is_delete = ?", adminId, addReq.Pid, addReq.Name, 0).Limit(1).First(&cate)
if cate.ID > 0 {
return response.AssertArgumentError.SetMessage("分类已存在!")
}
convert_util.Copy(&cate, addReq) convert_util.Copy(&cate, addReq)
cate.AdminId = adminId cate.AdminId = adminId
err := albSrv.db.Create(&cate).Error err := albSrv.db.Create(&cate).Error
e = response.CheckErr(err, "Cate添加失败") e = response.CheckErr(err, "分类添加失败")
return return
} }
@@ -196,12 +199,19 @@ func (albSrv albumService) CateRename(id uint, name string) (e error) {
if e = response.CheckErrDBNotRecord(err, "分类已不存在!"); e != nil { if e = response.CheckErrDBNotRecord(err, "分类已不存在!"); e != nil {
return return
} }
if e = response.CheckErr(err, "CateRename First err"); e != nil { if e = response.CheckErr(err, "重命名失败"); e != nil {
return return
} }
var cate2 common_model.AlbumCate
// 查询分类是否存在
albSrv.db.Where("admin_id = ? AND pid=? AND name = ? AND is_delete = ? AND id <> ?", cate.AdminId, cate.Pid, name, 0, cate.ID).Limit(1).First(&cate2)
if cate2.ID > 0 {
return response.AssertArgumentError.SetMessage("分类“" + name + "”已存在!")
}
cate.Name = name cate.Name = name
err = albSrv.db.Save(&cate).Error err = albSrv.db.Save(&cate).Error
e = response.CheckErr(err, "CateRename Save err") e = response.CheckErr(err, "分类重命名失败")
return return
} }
@@ -212,11 +222,11 @@ func (albSrv albumService) CateDel(id uint) (e error) {
if e = response.CheckErrDBNotRecord(err, "分类已不存在!"); e != nil { if e = response.CheckErrDBNotRecord(err, "分类已不存在!"); e != nil {
return return
} }
if e = response.CheckErr(err, "Cate待删除数据查找失败"); e != nil { if e = response.CheckErr(err, "待删除数据查找失败"); e != nil {
return return
} }
r := albSrv.db.Where("cid = ? AND is_delete = ?", id, 0).Limit(1).Find(&common_model.Album{}) r := albSrv.db.Where("cid = ? AND is_delete = ?", id, 0).Limit(1).Find(&common_model.Album{})
if e = response.CheckErr(r.Error, "CateDel Find err"); e != nil { if e = response.CheckErr(r.Error, "分类数据使用失败"); e != nil {
return return
} }
if r.RowsAffected > 0 { if r.RowsAffected > 0 {

View File

@@ -66,7 +66,7 @@
class="ml-3 mr-3" class="ml-3 mr-3"
:url="{{{.ModuleName}}}_import_file" :url="{{{.ModuleName}}}_import_file"
:data="{ cid: 0 }" :data="{ cid: 0 }"
type="file" :ext="['xlsx']"
:show-progress="true" :show-progress="true"
@change="resetPage" @change="resetPage"
> >

View File

@@ -42,9 +42,9 @@ func (service userProtocolService) GetModel(listReq schema.UserProtocolListReq)
if listReq.Content != nil { if listReq.Content != nil {
dbModel = dbModel.Where("content = ?", *listReq.Content) dbModel = dbModel.Where("content = ?", *listReq.Content)
} }
if listReq.Sort != nil { // if listReq.Sort != nil {
dbModel = dbModel.Where("sort = ?", *listReq.Sort) // dbModel = dbModel.Where("sort = ?", *listReq.Sort)
} // }
if listReq.CreateTimeStart != nil { if listReq.CreateTimeStart != nil {
dbModel = dbModel.Where("create_time >= ?", *listReq.CreateTimeStart) dbModel = dbModel.Where("create_time >= ?", *listReq.CreateTimeStart)
} }